diff --git a/Assets/Plugins/JSL/JoyShockLibrary.cs b/Assets/Plugins/JSL/JoyShockLibrary.cs index 20cddac06..73acb8037 100644 --- a/Assets/Plugins/JSL/JoyShockLibrary.cs +++ b/Assets/Plugins/JSL/JoyShockLibrary.cs @@ -43,11 +43,13 @@ public static class JSL public const int SplitFull = 3; // PS5 Player maps for the DS Player Lightbar - public const int DS5P1 = 4; - public const int DS5P2 = 10; - public const int DS5P3 = 21; - public const int DS5P4 = 27; - public const int DS5P5 = 31; + public static readonly int[] DualSensePlayerMask = { + 4, + 10, + 21, + 27, + 31 + }; [StructLayout(LayoutKind.Sequential)] public struct JOY_SHOCK_STATE @@ -100,6 +102,8 @@ public static class JSL public delegate void EventCallback(int handle, JOY_SHOCK_STATE state, JOY_SHOCK_STATE lastState, IMU_STATE imuState, IMU_STATE lastImuState, float deltaTime); + + public delegate void TouchCallback(int handle, TOUCH_STATE state, TOUCH_STATE lastState, float deltaTime); [DllImport("JoyShockLibrary")] public static extern int JslConnectDevices(); @@ -147,7 +151,7 @@ public static class JSL [DllImport("JoyShockLibrary")] public static extern void JslSetCallback(EventCallback callback); [DllImport("JoyShockLibrary")] - public static extern void JslSetTouchCallback(EventCallback callback); + public static extern void JslSetTouchCallback(TouchCallback callback); [DllImport("JoyShockLibrary")] public static extern int JslGetControllerType(int deviceId); diff --git a/Assets/Plugins/JSL/x64/libJoyShockLibrary.so b/Assets/Plugins/JSL/x64/libJoyShockLibrary.so new file mode 100644 index 000000000..e8d0a5152 Binary files /dev/null and b/Assets/Plugins/JSL/x64/libJoyShockLibrary.so differ diff --git a/Assets/Plugins/JSL/x64/libJoyShockLibrary.so.meta b/Assets/Plugins/JSL/x64/libJoyShockLibrary.so.meta new file mode 100644 index 000000000..92c1e3709 --- /dev/null +++ b/Assets/Plugins/JSL/x64/libJoyShockLibrary.so.meta @@ -0,0 +1,27 @@ +fileFormatVersion: 2 +guid: c785dbfac2c67974fa1cce056df6404d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/GameManager.cs b/Assets/Scripts/GameManager.cs index f09a73af1..c59834950 100644 --- a/Assets/Scripts/GameManager.cs +++ b/Assets/Scripts/GameManager.cs @@ -198,7 +198,7 @@ namespace HeavenStudio // LateUpdate works a bit better(?) but causes some bugs (like issues with bop animations). private void Update() { - PlayerInput.UpdateJoyShocks(); + PlayerInput.UpdateInputControllers(); if (BeatmapEntities() < 1) //bruh really you forgot to ckeck tempo changes return; @@ -528,12 +528,5 @@ namespace HeavenStudio { HeavenStudio.GameCamera.ResetAdditionalTransforms(); } - - void OnApplicationQuit() - { - Debug.Log("Disconnecting JoyShocks..."); - JSL.JslSetCallback(null); - JSL.JslDisconnectAndDisposeAll(); - } } } \ No newline at end of file diff --git a/Assets/Scripts/GlobalGameManager.cs b/Assets/Scripts/GlobalGameManager.cs index 64afc14e1..5492cab12 100644 --- a/Assets/Scripts/GlobalGameManager.cs +++ b/Assets/Scripts/GlobalGameManager.cs @@ -59,6 +59,7 @@ namespace HeavenStudio DontDestroyOnLoad(this.gameObject); instance = this; Starpelly.OS.ChangeWindowTitle("Heaven Studio DEMO"); + PlayerInput.InitInputControllers(); } public static GameObject CreateFade() @@ -141,5 +142,11 @@ namespace HeavenStudio MasterVolume = value; AudioListener.volume = MasterVolume; } + + void OnApplicationQuit() + { + Debug.Log("Disconnecting JoyShocks..."); + PlayerInput.DisconnectJoyshocks(); + } } } diff --git a/Assets/Scripts/InputSystem.meta b/Assets/Scripts/InputSystem.meta new file mode 100644 index 000000000..ba6524269 --- /dev/null +++ b/Assets/Scripts/InputSystem.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f0c2ca50a4b8a1b499a3efd717f1daaa +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/InputSystem/ControllerTypes.meta b/Assets/Scripts/InputSystem/ControllerTypes.meta new file mode 100644 index 000000000..721163c81 --- /dev/null +++ b/Assets/Scripts/InputSystem/ControllerTypes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dc30ca18de2bbc24d984b036097fe60d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/InputSystem/ControllerTypes/InputJoyshock.cs b/Assets/Scripts/InputSystem/ControllerTypes/InputJoyshock.cs new file mode 100644 index 000000000..d2cf11159 --- /dev/null +++ b/Assets/Scripts/InputSystem/ControllerTypes/InputJoyshock.cs @@ -0,0 +1,258 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using HeavenStudio.Util; + +using static JSL; + +namespace HeavenStudio.InputSystem +{ + public class InputJoyshock : InputController + { + static string[] joyShockNames = + { + "Unknown", + "Joy-Con (L)", + "Joy-Con (R)", + "Pro Controller", + "DualShock 4", + "DualSense" + }; + + int[] mappings = new int[] + { + ButtonMaskUp, + ButtonMaskDown, + ButtonMaskLeft, + ButtonMaskRight, + ButtonMaskS, + ButtonMaskE, + ButtonMaskW, + ButtonMaskN, + ButtonMaskL, + ButtonMaskR, + ButtonMaskPlus, + }; + + int joyshockHandle; + int type; + int splitType; + string joyshockName; + + InputDirection hatDirectionCurrent; + InputDirection hatDirectionLast; + + //buttons, sticks, triggers + JOY_SHOCK_STATE joyBtStateCurrent, joyBtStateLast; + //gyro and accelerometer + IMU_STATE joyImuStateCurrent, joyImuStateLast; + //touchpad + TOUCH_STATE joyTouchStateCurrent, joyTouchStateLast; + + InputJoyshock otherHalf; + + public InputJoyshock(int handle) + { + joyshockHandle = handle; + } + + public override void InitializeController() + { + joyBtStateCurrent = new JOY_SHOCK_STATE(); + joyBtStateLast = new JOY_SHOCK_STATE(); + joyImuStateCurrent = new IMU_STATE(); + joyImuStateLast = new IMU_STATE(); + joyTouchStateCurrent = new TOUCH_STATE(); + joyTouchStateLast = new TOUCH_STATE(); + + //FUTURE: remappable controls + + type = JslGetControllerType(joyshockHandle); + joyshockName = joyShockNames[type]; + + splitType = JslGetControllerSplitType(joyshockHandle); + } + + public override void UpdateState() + { + joyBtStateLast = joyBtStateCurrent; + joyBtStateCurrent = JslGetSimpleState(joyshockHandle); + } + + public override string GetDeviceName() + { + if (otherHalf != null) + return "Joy-Con Pair"; + return joyshockName; + } + + public override InputFeatures GetFeatures() + { + InputFeatures features = InputFeatures.Style_Pad | InputFeatures.Style_Baton; + switch (type) + { + case TypeJoyConLeft: + features |= InputFeatures.Readable_ShellColour | InputFeatures.Readable_ButtonColour | InputFeatures.Writable_PlayerLED | InputFeatures.Extra_SplitControllerLeft | InputFeatures.Extra_HDRumble; + break; + case TypeJoyConRight: + features |= InputFeatures.Readable_ShellColour | InputFeatures.Readable_ButtonColour | InputFeatures.Writable_PlayerLED | InputFeatures.Extra_SplitControllerRight | InputFeatures.Extra_HDRumble; + break; + case TypeProController: + features |= InputFeatures.Readable_ShellColour | InputFeatures.Readable_ButtonColour | InputFeatures.Readable_LeftGripColour | InputFeatures.Readable_RightGripColour | InputFeatures.Writable_PlayerLED | InputFeatures.Extra_HDRumble; + break; + case TypeDualShock4: + features |= InputFeatures.Readable_AnalogueTriggers | InputFeatures.Readable_Pointer | InputFeatures.Writable_LightBar; + break; + case TypeDualSense: + features |= InputFeatures.Readable_AnalogueTriggers | InputFeatures.Readable_Pointer | InputFeatures.Writable_PlayerLED | InputFeatures.Writable_LightBar; + break; + } + features |= InputFeatures.Readable_MotionSensor | InputFeatures.Extra_Rumble | InputFeatures.Style_Pad | InputFeatures.Style_Baton | InputFeatures.Style_Touch; + return features; + } + + public override int GetLastButtonDown() + { + return BitwiseUtils.FirstSetBit(joyBtStateCurrent.buttons & joyBtStateLast.buttons); + } + + public override KeyCode GetLastKeyDown() + { + return KeyCode.None; + } + + public override bool GetButton(int button) + { + return BitwiseUtils.WantCurrent(joyBtStateCurrent.buttons, mappings[button]); + } + + public override bool GetButtonDown(int button) + { + return BitwiseUtils.WantCurrentAndNotLast(joyBtStateCurrent.buttons, joyBtStateLast.buttons, mappings[button]); + } + + public override bool GetButtonUp(int button) + { + return BitwiseUtils.WantNotCurrentAndLast(joyBtStateCurrent.buttons, joyBtStateLast.buttons, mappings[button]); + } + + public override float GetAxis(InputAxis axis) + { + switch (axis) + { + case InputAxis.AxisLTrigger: + return joyBtStateCurrent.lTrigger; + case InputAxis.AxisRTrigger: + return joyBtStateCurrent.rTrigger; + case InputAxis.AxisLStickX: + return joyBtStateCurrent.stickLX; + case InputAxis.AxisLStickY: + return joyBtStateCurrent.stickLY; + case InputAxis.AxisRStickX: + return joyBtStateCurrent.stickRX; + case InputAxis.AxisRStickY: + return joyBtStateCurrent.stickRY; + case InputAxis.TouchpadX: //isn't updated for now, so always returns 0f + //return joyTouchStateCurrent.t0X; + case InputAxis.TouchpadY: + //return joyTouchStateCurrent.t0Y; + default: + return 0f; + } + } + + public override bool GetHatDirection(InputDirection direction) + { + //todo: check analogue stick hat direction too + switch (direction) + { + case InputDirection.Up: + return BitwiseUtils.WantCurrent(joyBtStateCurrent.buttons, ButtonMaskUp); + case InputDirection.Down: + return BitwiseUtils.WantCurrent(joyBtStateCurrent.buttons, ButtonMaskDown); + case InputDirection.Left: + return BitwiseUtils.WantCurrent(joyBtStateCurrent.buttons, ButtonMaskLeft); + case InputDirection.Right: + return BitwiseUtils.WantCurrent(joyBtStateCurrent.buttons, ButtonMaskRight); + default: + return false; + } + } + + public override bool GetHatDirectionDown(InputDirection direction) + { + //todo: check analogue stick hat direction too + switch (direction) + { + case InputDirection.Up: + return BitwiseUtils.WantCurrentAndNotLast(joyBtStateCurrent.buttons, joyBtStateLast.buttons, ButtonMaskUp); + case InputDirection.Down: + return BitwiseUtils.WantCurrentAndNotLast(joyBtStateCurrent.buttons, joyBtStateLast.buttons, ButtonMaskDown); + case InputDirection.Left: + return BitwiseUtils.WantCurrentAndNotLast(joyBtStateCurrent.buttons, joyBtStateLast.buttons, ButtonMaskLeft); + case InputDirection.Right: + return BitwiseUtils.WantCurrentAndNotLast(joyBtStateCurrent.buttons, joyBtStateLast.buttons, ButtonMaskRight); + default: + return false; + } + } + + public override bool GetHatDirectionUp(InputDirection direction) + { + //todo: check analogue stick hat direction too + switch (direction) + { + case InputDirection.Up: + return BitwiseUtils.WantNotCurrentAndLast(joyBtStateCurrent.buttons, joyBtStateLast.buttons, ButtonMaskUp); + case InputDirection.Down: + return BitwiseUtils.WantNotCurrentAndLast(joyBtStateCurrent.buttons, joyBtStateLast.buttons, ButtonMaskDown); + case InputDirection.Left: + return BitwiseUtils.WantNotCurrentAndLast(joyBtStateCurrent.buttons, joyBtStateLast.buttons, ButtonMaskLeft); + case InputDirection.Right: + return BitwiseUtils.WantNotCurrentAndLast(joyBtStateCurrent.buttons, joyBtStateLast.buttons, ButtonMaskRight); + default: + return false; + } + } + + public override void SetPlayer(int playerNum) + { + if (playerNum == -1) + { + this.playerNum = null; + JslSetPlayerNumber(joyshockHandle, 0); + return; + } + if (type == TypeDualSense) + { + if (playerNum <= 4) + { + playerNum = DualSensePlayerMask[playerNum]; + } + } + JslSetPlayerNumber(joyshockHandle, playerNum); + this.playerNum = playerNum; + } + + public override int? GetPlayer() + { + return this.playerNum; + } + + public int GetHandle() + { + return joyshockHandle; + } + + public void DisconnectJoyshock() + { + if (otherHalf != null) + { + otherHalf = null; + } + JslSetRumble(joyshockHandle, 0, 0); + JslSetLightColour(joyshockHandle, 0); + JslSetPlayerNumber(joyshockHandle, 0); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/InputSystem/ControllerTypes/InputJoyshock.cs.meta b/Assets/Scripts/InputSystem/ControllerTypes/InputJoyshock.cs.meta new file mode 100644 index 000000000..e89224df0 --- /dev/null +++ b/Assets/Scripts/InputSystem/ControllerTypes/InputJoyshock.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: abe9a6a60f8629440a3cae605aca60a3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/InputSystem/ControllerTypes/InputKeyboard.cs b/Assets/Scripts/InputSystem/ControllerTypes/InputKeyboard.cs new file mode 100644 index 000000000..601bf8547 --- /dev/null +++ b/Assets/Scripts/InputSystem/ControllerTypes/InputKeyboard.cs @@ -0,0 +1,154 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +using static JSL; + +namespace HeavenStudio.InputSystem +{ + public class InputKeyboard : InputController + { + static KeyCode[] keyCodes = (KeyCode[]) System.Enum.GetValues(typeof(UnityEngine.KeyCode)); + + //FUTURE: remappable controls + //KeyCode[] mappings = new KeyCode[Enum.GetNames(typeof(ButtonsPad)).Length]; + KeyCode[] mappings = new KeyCode[] + { + KeyCode.UpArrow, + KeyCode.DownArrow, + KeyCode.LeftArrow, + KeyCode.RightArrow, + KeyCode.X, + KeyCode.Z, + KeyCode.C, + KeyCode.V, + KeyCode.S, + KeyCode.D, + KeyCode.Return, + }; + + InputDirection hatDirectionCurrent; + InputDirection hatDirectionLast; + + public override void InitializeController() + { + //FUTURE: remappable controls + } + + public override void UpdateState() + { + // Update the state of the controller + } + + public override string GetDeviceName() + { + return "Keyboard"; + } + + public override InputFeatures GetFeatures() + { + return InputFeatures.Readable_StringInput | InputFeatures.Style_Pad | InputFeatures.Style_Baton; + } + + public override int GetLastButtonDown() + { + return 0; + } + + public override KeyCode GetLastKeyDown() + { + for(KeyCode i = keyCodes[1]; i <= KeyCode.Menu; i++) { + if (Input.GetKeyDown(i)) + return i; + } + return KeyCode.None; + } + + public override bool GetButton(int button) + { + return Input.GetKey(mappings[button]); + } + + public override bool GetButtonDown(int button) + { + return Input.GetKeyDown(mappings[button]); + } + + public override bool GetButtonUp(int button) + { + return Input.GetKeyUp(mappings[button]); + } + + public override float GetAxis(InputAxis axis) + { + return 0; + } + + //todo: directionals + public override bool GetHatDirection(InputDirection direction) + { + switch (direction) + { + case InputDirection.Up: + return Input.GetKey(KeyCode.UpArrow); + case InputDirection.Down: + return Input.GetKey(KeyCode.DownArrow); + case InputDirection.Left: + return Input.GetKey(KeyCode.LeftArrow); + case InputDirection.Right: + return Input.GetKey(KeyCode.RightArrow); + default: + return false; + } + } + + public override bool GetHatDirectionDown(InputDirection direction) + { + switch (direction) + { + case InputDirection.Up: + return Input.GetKeyDown(KeyCode.UpArrow); + case InputDirection.Down: + return Input.GetKeyDown(KeyCode.DownArrow); + case InputDirection.Left: + return Input.GetKeyDown(KeyCode.LeftArrow); + case InputDirection.Right: + return Input.GetKeyDown(KeyCode.RightArrow); + default: + return false; + } + } + + public override bool GetHatDirectionUp(InputDirection direction) + { + switch (direction) + { + case InputDirection.Up: + return Input.GetKeyUp(KeyCode.UpArrow); + case InputDirection.Down: + return Input.GetKeyUp(KeyCode.DownArrow); + case InputDirection.Left: + return Input.GetKeyUp(KeyCode.LeftArrow); + case InputDirection.Right: + return Input.GetKeyUp(KeyCode.RightArrow); + default: + return false; + } + } + + public override void SetPlayer(int playerNum) + { + if (playerNum == -1) + { + this.playerNum = null; + return; + } + this.playerNum = playerNum; + } + + public override int? GetPlayer() + { + return playerNum; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/InputSystem/ControllerTypes/InputKeyboard.cs.meta b/Assets/Scripts/InputSystem/ControllerTypes/InputKeyboard.cs.meta new file mode 100644 index 000000000..038696ccd --- /dev/null +++ b/Assets/Scripts/InputSystem/ControllerTypes/InputKeyboard.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 19bccc8eebf390943a5c6d8bc59f4c7f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/InputSystem/InputController.cs b/Assets/Scripts/InputSystem/InputController.cs new file mode 100644 index 000000000..5996f23ed --- /dev/null +++ b/Assets/Scripts/InputSystem/InputController.cs @@ -0,0 +1,168 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace HeavenStudio.InputSystem +{ + /// + /// Generic class to allow adapting any type and combination of HIDs to a universal controller format. + /// Specifically designed for Heaven Studio, but can be adapted to any use. + /// + public abstract class InputController + { + //Buttons and Axis used by most controllers + public enum InputButtons : int + { + ButtonPadUp = 0, + ButtonPadDown = 1, + ButtonPadLeft = 2, + ButtonPadRight = 3, + ButtonPlus = 4, + ButtonOptions = 4, + ButtonMinus = 5, + ButtonShare = 5, + ButtonLClick = 6, + ButtonRClick = 7, + ButtonL = 8, + ButtonR = 9, + ButtonZL = 10, + ButtonZR = 11, + ButtonFaceS = 12, + ButtonFaceE = 13, + ButtonFaceW = 14, + ButtonFaceN = 15, + ButtonHome = 16, + ButtonPS = 16, + ButtonCapture = 17, + ButtonTouchpadClick = 17, + ButtonSL = 18, + ButtonSR = 19, + } + public enum InputAxis : int + { + AxisLTrigger = 4, + AxisRTrigger = 5, + AxisLStickX = 0, + AxisLStickY = 1, + AxisRStickX = 2, + AxisRStickY = 3, + TouchpadX = 6, + TouchpadY = 7 + } + + //D-Pad directions, usable to adapt analogue sticks to cardinal directions + public enum InputDirection : int + { + Up = 0, + Right = 1, + Down = 2, + Left = 3, + } + + //Common specific controller features + [System.Flags] + public enum InputFeatures + { + //readable properties + Readable_ShellColour, + Readable_ButtonColour, + Readable_LeftGripColour, + Readable_RightGripColour, + Readable_AnalogueTriggers, + Readable_StringInput, + Readable_Pointer, + Readable_MotionSensor, + + //writable properties + Writable_PlayerLED, + Writable_LightBar, + Writable_Chroma, + Writable_Speaker, + + //other / "special" properties + Extra_SplitControllerLeft, + Extra_SplitControllerRight, + Extra_Rumble, + Extra_HDRumble, + + //supported control styles + Style_Pad, + Style_Baton, + Style_Touch + }; + + //Following enums are specific to Heaven Studio, can be removed in other applications + //Control styles in Heaven Studio + public enum ControlStyles + { + Pad, + Baton, + Touch + } + + //buttons used in Heaven Studio gameplay (Pad Style) + public enum ButtonsPad : int + { + PadUp = 0, + PadDown = 1, + PadLeft = 2, + PadRight = 3, + PadS = 4, + PadE = 5, + PadW = 6, + PadN = 7, + PadL = 8, + PadR = 9, + PadPause = 10, + } + + //FUTURE: buttons used in Heaven Studio gameplay ("Form Baton" / WiiMote Style) + public enum ButtonsBaton : int + { + BatonS = 0, //-- all these... + BatonE = 1, // | + BatonW = 2, // | + BatonN = 3, //-- + BatonA = 4, // < ...map to this, but are directional + BatonB = 5, // should never be used alone + Baton1 = 6, + Baton2 = 7, + BatonPause = 8, + } + + //FUTURE: buttons used in Heaven Studio gameplay (Touch Style) + public enum ButtonsTouch : int + { + TouchL = 0, + TouchR = 1, + TouchTap = 2, + TouchFlick = 3, + TouchButtonL = 4, + TouchButtonR = 4, + } + + protected int? playerNum; + + public abstract void InitializeController(); + public abstract void UpdateState(); // Update the state of the controller + + public abstract string GetDeviceName(); // Get the name of the controller + public abstract InputFeatures GetFeatures(); // Get the features of the controller + + public abstract int GetLastButtonDown(); // Get the last button down + public abstract KeyCode GetLastKeyDown(); // Get the last key down (used for keyboards and other devices that use Keycode) + public abstract bool GetButton(int button); // is button currently pressed? + public abstract bool GetButtonDown(int button); // is button just pressed? + public abstract bool GetButtonUp(int button); // is button just released? + public abstract float GetAxis(InputAxis axis); // Get the value of an axis + public abstract bool GetHatDirection(InputDirection direction); // is direction active? + public abstract bool GetHatDirectionDown(InputDirection direction); // direction just became active? + public abstract bool GetHatDirectionUp(InputDirection direction); // direction just became inactive? + + public abstract void SetPlayer(int playerNum); // Set the player number (starts at 1, set to -1 or null for no player) + public abstract int? GetPlayer(); // Get the player number (null if no player) + + //public abstract Sprite GetDisplayIcon(); //"big icon" for the controller in the settings menu + //public abstract Sprite GetPlaybackIcon(); //"small icon" for the controller during playback + } +} \ No newline at end of file diff --git a/Assets/Scripts/InputSystem/InputController.cs.meta b/Assets/Scripts/InputSystem/InputController.cs.meta new file mode 100644 index 000000000..56dcf1177 --- /dev/null +++ b/Assets/Scripts/InputSystem/InputController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f940937684f598749af06c1297727c4b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/InputSystem/PlayerInput.cs b/Assets/Scripts/InputSystem/PlayerInput.cs new file mode 100644 index 000000000..7b50cdaa1 --- /dev/null +++ b/Assets/Scripts/InputSystem/PlayerInput.cs @@ -0,0 +1,227 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using HeavenStudio.InputSystem; + +using static JSL; + +namespace HeavenStudio +{ + public class PlayerInput + { + //Clockwise + public const int UP = 0; + public const int RIGHT = 1; + public const int DOWN = 2; + public const int LEFT = 3; + + /////////////////////////////// + ////TEMPORARY JSL FUNCTIONS//// + /////////////////////////////// + + static int jslDevicesFound = 0; + static int jslDevicesConnected = 0; + static int[] jslDeviceHandles; + + static List inputDevices; + + public static int InitInputControllers() + { + inputDevices = new List(); + //Keyboard setup + InputKeyboard keyboard = new InputKeyboard(); + keyboard.SetPlayer(1); + keyboard.InitializeController(); + inputDevices.Add(keyboard); + //end Keyboard setup + + //JoyShock setup + Debug.Log("Flushing possible JoyShocks..."); + DisconnectJoyshocks(); + + jslDevicesFound = JslConnectDevices(); + if (jslDevicesFound > 0) + { + jslDeviceHandles = new int[jslDevicesFound]; + jslDevicesConnected = JslGetConnectedDeviceHandles(jslDeviceHandles, jslDevicesFound); + if (jslDevicesConnected < jslDevicesFound) + { + Debug.Log("Found " + jslDevicesFound + " JoyShocks, but only " + jslDevicesConnected + " are connected."); + } + else + { + Debug.Log("Found " + jslDevicesFound + " JoyShocks."); + Debug.Log("Connected " + jslDevicesConnected + " JoyShocks."); + } + + foreach (int i in jslDeviceHandles) + { + Debug.Log("Setting up JoyShock: ( Handle " + i + ", type " + JslGetControllerType(i) + " )"); + InputJoyshock joyshock = new InputJoyshock(i); + joyshock.InitializeController(); + joyshock.SetPlayer(inputDevices.Count + 1); + } + } + else + { + Debug.Log("No JoyShocks found."); + } + //end JoyShock setup + + //TODO: XInput setup (boo) + //end XInput setup + + return inputDevices.Count; + } + + public static int GetNumControllersConnected() + { + return inputDevices.Count; + } + + public static List GetInputControllers() + { + return inputDevices; + } + + public static InputController GetInputController(int player) + { + //select input controller that has player field set to player + //this will return the first controller that has that player number in the case of controller pairs (eg. Joy-Cons) + //so such controllers should have a reference to the other controller in the pair + foreach (InputController i in inputDevices) + { + if (i.GetPlayer() == player) + { + return i; + } + } + return null; + } + + public static void UpdateInputControllers() + { + foreach (InputController i in inputDevices) + { + i.UpdateState(); + } + } + + public static void DisconnectJoyshocks() + { + if (jslDeviceHandles != null && jslDevicesConnected > 0 && jslDeviceHandles.Length > 0) + { + foreach (InputController i in inputDevices) + { + if (typeof(InputJoyshock) == i.GetType()) + { + InputJoyshock joy = (InputJoyshock)i; + joy.DisconnectJoyshock(); + } + } + } + JslDisconnectAndDisposeAll(); + jslDevicesFound = 0; + jslDevicesConnected = 0; + } + + // The autoplay isn't activated AND + // The song is actually playing AND + // The GameManager allows you to Input + public static bool playerHasControl() + { + return !GameManager.instance.autoplay && Conductor.instance.isPlaying && GameManager.instance.canInput; + } + + /*--------------------*/ + /* MAIN INPUT METHODS */ + /*--------------------*/ + + // BUTTONS + //TODO: refactor for controller and custom binds, currently uses temporary button checks + + public static bool Pressed(bool includeDPad = false) + { + bool keyDown = GetInputController(1).GetButtonDown((int) InputController.ButtonsPad.PadE) || (includeDPad && GetAnyDirectionDown()); + return keyDown && !GameManager.instance.autoplay && Conductor.instance.isPlaying && GameManager.instance.canInput ; + } + + public static bool PressedUp(bool includeDPad = false) + { + bool keyUp = GetInputController(1).GetButtonUp((int) InputController.ButtonsPad.PadE) || (includeDPad && GetAnyDirectionUp()); + return keyUp && !GameManager.instance.autoplay && Conductor.instance.isPlaying && GameManager.instance.canInput; + } + + public static bool Pressing(bool includeDPad = false) + { + bool pressing = GetInputController(1).GetButton((int) InputController.ButtonsPad.PadE) || (includeDPad && GetAnyDirection()); + return pressing && !GameManager.instance.autoplay && Conductor.instance.isPlaying && GameManager.instance.canInput; + } + + + public static bool AltPressed() + { + bool down = GetInputController(1).GetButtonDown((int) InputController.ButtonsPad.PadS); + return down && playerHasControl(); + } + + public static bool AltPressedUp() + { + bool up = GetInputController(1).GetButtonUp((int) InputController.ButtonsPad.PadS); + return up && playerHasControl(); + } + + public static bool AltPressing() + { + bool pressing = GetInputController(1).GetButton((int) InputController.ButtonsPad.PadS); + return pressing && playerHasControl(); + } + + //Directions + + public static bool GetAnyDirectionDown() + { + return (GetInputController(1).GetButtonDown((int) InputController.ButtonsPad.PadUp) + || GetInputController(1).GetButtonDown((int) InputController.ButtonsPad.PadDown) + || GetInputController(1).GetButtonDown((int) InputController.ButtonsPad.PadLeft) + || GetInputController(1).GetButtonDown((int) InputController.ButtonsPad.PadRight) + ) && playerHasControl(); + + } + + public static bool GetAnyDirectionUp() + { + return (GetInputController(1).GetButtonUp((int) InputController.ButtonsPad.PadUp) + || GetInputController(1).GetButtonUp((int) InputController.ButtonsPad.PadDown) + || GetInputController(1).GetButtonUp((int) InputController.ButtonsPad.PadLeft) + || GetInputController(1).GetButtonUp((int) InputController.ButtonsPad.PadRight) + ) && playerHasControl(); + + } + + public static bool GetAnyDirection() + { + return (GetInputController(1).GetButton((int) InputController.ButtonsPad.PadUp) + || GetInputController(1).GetButton((int) InputController.ButtonsPad.PadDown) + || GetInputController(1).GetButton((int) InputController.ButtonsPad.PadLeft) + || GetInputController(1).GetButton((int) InputController.ButtonsPad.PadRight) + ) && playerHasControl(); + + } + + public static bool GetSpecificDirection(int direction) + { + return GetInputController(1).GetHatDirection((InputController.InputDirection) direction) && playerHasControl(); + } + + public static bool GetSpecificDirectionDown(int direction) + { + return GetInputController(1).GetHatDirectionDown((InputController.InputDirection) direction) && playerHasControl(); + } + + public static bool GetSpecificDirectionUp(int direction) + { + return GetInputController(1).GetHatDirectionUp((InputController.InputDirection) direction) && playerHasControl(); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/PlayerInput.cs.meta b/Assets/Scripts/InputSystem/PlayerInput.cs.meta similarity index 83% rename from Assets/Scripts/PlayerInput.cs.meta rename to Assets/Scripts/InputSystem/PlayerInput.cs.meta index 831b948c3..dcfc46fb9 100644 --- a/Assets/Scripts/PlayerInput.cs.meta +++ b/Assets/Scripts/InputSystem/PlayerInput.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 9503627b14bba414cae0fbc5da9e4120 +guid: ff41ce113a3b89f4892e362c8ef3d773 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/Scripts/LevelEditor/SettingsDialog/Tabs/ControllerSettings.cs b/Assets/Scripts/LevelEditor/SettingsDialog/Tabs/ControllerSettings.cs index f36340623..721f99ed3 100644 --- a/Assets/Scripts/LevelEditor/SettingsDialog/Tabs/ControllerSettings.cs +++ b/Assets/Scripts/LevelEditor/SettingsDialog/Tabs/ControllerSettings.cs @@ -1,8 +1,12 @@ +using System; +using System.Collections; +using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using TMPro; using HeavenStudio; +using HeavenStudio.InputSystem; using static JSL; namespace HeavenStudio.Editor @@ -11,13 +15,52 @@ namespace HeavenStudio.Editor { [SerializeField] private TMP_Text numConnectedLabel; [SerializeField] private TMP_Text currentControllerLabel; + [SerializeField] private TMP_Dropdown controllersDropdown; + [SerializeField] private TMP_Dropdown splitControllersDropdown; + + private void Start() { + numConnectedLabel.text = "Connected: " + PlayerInput.GetNumControllersConnected(); + currentControllerLabel.text = "Current Controller: " + PlayerInput.GetInputController(1).GetDeviceName(); + } public void SearchAndConnectControllers() { - int connected = PlayerInput.InitJoyShocks(); + int connected = PlayerInput.InitInputControllers(); numConnectedLabel.text = "Connected: " + connected; - //do this better - currentControllerLabel.text = "Current Controller: " + PlayerInput.GetJoyShockName(0); + currentControllerLabel.text = "Current Controller: " + PlayerInput.GetInputController(1).GetDeviceName(); + } + + public void populateControllersDropdown() + { + List dropDownData = new List(); + var vals = PlayerInput.GetInputControllers(); + for (int i = 0; i < vals.Count; i++) + { + TMP_Dropdown.OptionData optionData = new TMP_Dropdown.OptionData(); + optionData.text = vals[i].GetDeviceName(); + dropDownData.Add(optionData); + } + controllersDropdown.AddOptions(dropDownData); + controllersDropdown.value = 0; + } + + public void populateSplitControllersDropdown() + { + List dropDownData = new List(); + var vals = PlayerInput.GetInputControllers(); + InputController.InputFeatures features; + for (int i = 0; i < vals.Count; i++) + { + features = vals[i].GetFeatures(); + if (features.HasFlag(InputController.InputFeatures.Extra_SplitControllerLeft) || features.HasFlag(InputController.InputFeatures.Extra_SplitControllerRight)) + { + TMP_Dropdown.OptionData optionData = new TMP_Dropdown.OptionData(); + optionData.text = vals[i].GetDeviceName(); + dropDownData.Add(optionData); + } + } + splitControllersDropdown.AddOptions(dropDownData); + splitControllersDropdown.value = 0; } } } \ No newline at end of file diff --git a/Assets/Scripts/PlayerInput.cs b/Assets/Scripts/PlayerInput.cs deleted file mode 100644 index 7ebf0d8b8..000000000 --- a/Assets/Scripts/PlayerInput.cs +++ /dev/null @@ -1,319 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using UnityEngine; - -using static JSL; - -namespace HeavenStudio -{ - public class PlayerInput - { - /////////////////////////////// - ////TEMPORARY JSL FUNCTIONS//// - /////////////////////////////// - static string[] joyShockNames = - { - "Unknown", - "Joy-Con (L)", - "Joy-Con (R)", - "Pro Controller", - "DualShock 4", - "DualSense" - }; - - static int numDevicesFound = 0; - static int numDevicesConnected = 0; - static int[] deviceHandles; - - static Dictionary joyBtStateCurrent; - static Dictionary joyBtStateLast; - - static Dictionary joyImuStateCurrent; - static Dictionary joyImuStateLast; - - public static int InitJoyShocks() - { - //flush old joyshocks - Debug.Log("Flushing possible JoyShocks..."); - JslDisconnectAndDisposeAll(); - - numDevicesFound = 0; - numDevicesConnected = 0; - - numDevicesFound = JslConnectDevices(); - if (numDevicesFound > 0) - { - deviceHandles = new int[numDevicesFound]; - numDevicesConnected = JslGetConnectedDeviceHandles(deviceHandles, numDevicesFound); - joyBtStateCurrent = new Dictionary(); - joyBtStateLast = new Dictionary(); - if (numDevicesConnected < numDevicesFound) - { - Debug.Log("Found " + numDevicesFound + " JoyShocks, but only " + numDevicesConnected + " are connected."); - } - else - { - Debug.Log("Found " + numDevicesFound + " JoyShocks."); - Debug.Log("Connected " + numDevicesConnected + " JoyShocks."); - } - - foreach (int i in deviceHandles) - { - Debug.Log("Setting up JoyShock: " + joyShockNames[JslGetControllerType(i)] + " ( Player " + i + ", type " + JslGetControllerType(i) + " )"); - } - return numDevicesConnected; - } - else - { - Debug.Log("No JoyShocks found."); - return 0; - } - } - - public static string GetJoyShockName(int playerNum) - { - return joyShockNames[JslGetControllerType(deviceHandles[playerNum])]; - } - - public static void UpdateJoyShocks() - { - if (deviceHandles == null || numDevicesConnected == 0) return; - foreach (var id in deviceHandles) - { - if (joyBtStateCurrent.ContainsKey(id)) - { - joyBtStateLast[id] = joyBtStateCurrent[id]; - } - else - { - joyBtStateLast[id] = new JOY_SHOCK_STATE(); - } - joyBtStateCurrent[id] = JslGetSimpleState(id); - } - } - - //TODO: refactor to allow controller selection (and for split controllers, multiple controllers) - static bool GetJoyBtDown(int bt) - { - if (deviceHandles == null || numDevicesConnected <= 0) // <= player number in the future - { - return false; - } - bt = 1 << bt; - int p1Id = deviceHandles[0]; - try - { - int curBt = joyBtStateCurrent[p1Id].buttons; - int oldBt = joyBtStateLast[p1Id].buttons; - return ((curBt & bt) == bt) && ((oldBt & bt) != bt); - } - catch (System.Exception) - { - return false; - } - } - static bool GetJoyBt(int bt) - { - if (deviceHandles == null || numDevicesConnected <= 0) // <= player number in the future - { - return false; - } - bt = 1 << bt; - int p1Id = deviceHandles[0]; - try - { - int curBt = joyBtStateCurrent[p1Id].buttons; - return (curBt & bt) == bt; - } - catch (System.Exception) - { - return false; - } - - } - - static bool GetJoyBtUp(int bt) - { - if (deviceHandles == null || numDevicesConnected <= 0) // <= player number in the future - { - return false; - } - bt = 1 << bt; - int p1Id = deviceHandles[0]; - try - { - int curBt = joyBtStateCurrent[p1Id].buttons; - int oldBt = joyBtStateLast[p1Id].buttons; - return ((curBt & bt) != bt) && ((oldBt & bt) == bt); - } - catch (System.Exception) - { - return false; - } - - } - - ////END TEMPORARY JSL FUNCTIONS - /////////////////////////////// - - //Clockwise - public const int UP = 0; - public const int RIGHT = 1; - public const int DOWN = 2; - public const int LEFT = 3; - - // The autoplay isn't activated AND - // The song is actually playing AND - // The GameManager allows you to Input - public static bool playerHasControl() - { - return !GameManager.instance.autoplay && Conductor.instance.isPlaying && GameManager.instance.canInput; - } - - /*--------------------*/ - /* MAIN INPUT METHODS */ - /*--------------------*/ - - // BUTTONS - //TODO: refactor for controller and custom binds, currently uses temporary button checks - - public static bool Pressed(bool includeDPad = false) - { - bool keyDown = Input.GetKeyDown(KeyCode.Z) || GetJoyBtDown(ButtonMaskE) || (includeDPad && GetAnyDirectionDown()); - return keyDown && !GameManager.instance.autoplay && Conductor.instance.isPlaying && GameManager.instance.canInput ; - } - - public static bool PressedUp(bool includeDPad = false) - { - bool keyUp = Input.GetKeyUp(KeyCode.Z) || GetJoyBtUp(ButtonMaskE) || (includeDPad && GetAnyDirectionUp()); - return keyUp && !GameManager.instance.autoplay && Conductor.instance.isPlaying && GameManager.instance.canInput; - } - - public static bool Pressing(bool includeDPad = false) - { - bool pressing = Input.GetKey(KeyCode.Z) || GetJoyBt(ButtonMaskE) || (includeDPad && GetAnyDirection()); - return pressing && !GameManager.instance.autoplay && Conductor.instance.isPlaying && GameManager.instance.canInput; - } - - - public static bool AltPressed() - { - bool down = Input.GetKeyDown(KeyCode.X) || GetJoyBtDown(ButtonMaskS); - return down && playerHasControl(); - } - - public static bool AltPressedUp() - { - bool up = Input.GetKeyUp(KeyCode.X) || GetJoyBtUp(ButtonMaskS); - return up && playerHasControl(); - } - - public static bool AltPressing() - { - bool pressing = Input.GetKey(KeyCode.X) || GetJoyBt(ButtonMaskS); - return pressing && playerHasControl(); - } - - //Directions - - public static bool GetAnyDirectionDown() - { - return (Input.GetKeyDown(KeyCode.UpArrow) - || Input.GetKeyDown(KeyCode.DownArrow) - || Input.GetKeyDown(KeyCode.LeftArrow) - || Input.GetKeyDown(KeyCode.RightArrow) - - || GetJoyBtDown(ButtonMaskUp) - || GetJoyBtDown(ButtonMaskDown) - || GetJoyBtDown(ButtonMaskLeft) - || GetJoyBtDown(ButtonMaskRight) - ) && playerHasControl(); - - } - - public static bool GetAnyDirectionUp() - { - return (Input.GetKeyUp(KeyCode.UpArrow) - || Input.GetKeyUp(KeyCode.DownArrow) - || Input.GetKeyUp(KeyCode.LeftArrow) - || Input.GetKeyUp(KeyCode.RightArrow) - - || GetJoyBtUp(ButtonMaskUp) - || GetJoyBtUp(ButtonMaskDown) - || GetJoyBtUp(ButtonMaskLeft) - || GetJoyBtUp(ButtonMaskRight) - ) && playerHasControl(); - - } - - public static bool GetAnyDirection() - { - return (Input.GetKey(KeyCode.UpArrow) - || Input.GetKey(KeyCode.DownArrow) - || Input.GetKey(KeyCode.LeftArrow) - || Input.GetKey(KeyCode.RightArrow) - - || GetJoyBt(ButtonMaskUp) - || GetJoyBt(ButtonMaskDown) - || GetJoyBt(ButtonMaskLeft) - || GetJoyBt(ButtonMaskRight) - ) && playerHasControl(); - - } - - public static bool GetSpecificDirectionDown(int direction) - { - KeyCode targetCode = getKeyCode(direction); - if (targetCode == KeyCode.None) return false; - - int targetMask = getButtonMask(direction); - if (targetMask == 0) return false; - return (Input.GetKeyDown(targetCode) || GetJoyBtDown(targetMask)) && playerHasControl(); - } - - public static bool GetSpecificDirectionUp(int direction) - { - KeyCode targetCode = getKeyCode(direction); - if (targetCode == KeyCode.None) return false; - - int targetMask = getButtonMask(direction); - if (targetMask == 0) return false; - - return (Input.GetKeyUp(targetCode) || GetJoyBtUp(targetMask)) && playerHasControl(); - } - - - private static KeyCode getKeyCode(int direction) - { - KeyCode targetKeyCode; - - switch (direction) - { - case PlayerInput.UP: targetKeyCode = KeyCode.UpArrow; break; - case PlayerInput.DOWN: targetKeyCode = KeyCode.DownArrow; break; - case PlayerInput.LEFT: targetKeyCode = KeyCode.LeftArrow; break; - case PlayerInput.RIGHT: targetKeyCode = KeyCode.RightArrow; break; - default: targetKeyCode = KeyCode.None; break; - } - - return targetKeyCode; - } - - private static int getButtonMask(int direction) - { - int targetKeyCode; - - switch (direction) - { - case PlayerInput.UP: targetKeyCode = ButtonMaskUp; break; - case PlayerInput.DOWN: targetKeyCode = ButtonMaskDown; break; - case PlayerInput.LEFT: targetKeyCode = ButtonMaskLeft; break; - case PlayerInput.RIGHT: targetKeyCode = ButtonMaskRight; break; - default: targetKeyCode = -1; break; - } - - return targetKeyCode; - } - - } -} \ No newline at end of file diff --git a/Assets/Scripts/Util/BitwiseUtils.cs b/Assets/Scripts/Util/BitwiseUtils.cs new file mode 100644 index 000000000..df5c98b97 --- /dev/null +++ b/Assets/Scripts/Util/BitwiseUtils.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace HeavenStudio.Util +{ + public static class BitwiseUtils + { + /// + /// Returns the value of the lowest set bit in the given integer. + /// + /// The integer to check. + public static int FirstSetBit(int num) + { + return num & (-num); + } + + /// + /// Returns true if the wanted bit is set in the given integer. + /// + /// The integer to check. + /// The bit(s) to check for. + public static bool WantCurrent(int num, int want) + { + return (num & want) == want; + } + + /// + /// Returns true if the wanted bit is set in the first integer, and not in the second. + /// + /// The first integer to check. + /// The second integer to check. + /// The bit(s) to check for. + public static bool WantCurrentAndNotLast(int num1, int num2, int want) + { + return ((num1 & want) == want) && ((num2 & want) != want); + } + + /// + /// Returns true if the wanted bit is not set in the first integer, but set in the second. + /// + /// The first integer to check. + /// The second integer to check. + /// The bit(s) to check for. + public static bool WantNotCurrentAndLast(int num1, int num2, int want) + { + return ((num1 & want) != want) && ((num2 & want) == want); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Util/BitwiseUtils.cs.meta b/Assets/Scripts/Util/BitwiseUtils.cs.meta new file mode 100644 index 000000000..5b00a3b63 --- /dev/null +++ b/Assets/Scripts/Util/BitwiseUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 18f088a4c3c17b143a1985f3cee5d90a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: