commit 510a2961f6166ed0ab5e63c9ed920db618336b33 Author: RiddimCrunch Date: Sat Sep 20 00:28:00 2025 -0400 Package input arcade diff --git a/CHANGELOG.MD b/CHANGELOG.MD new file mode 100644 index 0000000..59160a9 --- /dev/null +++ b/CHANGELOG.MD @@ -0,0 +1,24 @@ +# Changelog + +All notable changes to this package will be documented in the notion. + +## [1.0.0] - 2025-09-20 + +### Added +- Initial release of Conjure Arcade Controller package +- Custom Input Device implementation for Conjure Arcade Controllers +- Support for arcade joystick with directional buttons +- Support for 8 buttons (Home, Start, 1-3, A-C) +- Multi-controller support with controller indexing +- Input Action Asset extension methods for easy controller assignment +- Custom input processors for stick normalization +- Comprehensive documentation and examples + +### Features +- `ConjureArcadeController` - Main controller device class +- `ConjureArcadeStickControl` - Custom stick control with directional buttons +- `ConjureArcadeControllerState` - HID input state definition +- `ConjureInputSystem` - Automatic registration and initialization +- `InputActionAssetExtension` - Helper methods for controller assignment +- Controller detection and management system +- Event system for controller connect/disconnect \ No newline at end of file diff --git a/CHANGELOG.MD.meta b/CHANGELOG.MD.meta new file mode 100644 index 0000000..694a0bd --- /dev/null +++ b/CHANGELOG.MD.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 53be246cbe313b7478b9302289a1e5ea +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Documentation.meta b/Documentation.meta new file mode 100644 index 0000000..182bbea --- /dev/null +++ b/Documentation.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5727b7fcbb3ce444ca212cbaf2c56dd3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/README.md b/README.md new file mode 100644 index 0000000..5a487ff --- /dev/null +++ b/README.md @@ -0,0 +1,174 @@ +# Conjure Arcade Controller Package + +Unity Input System support for Conjure Arcade Controllers. + +## Features + +- Custom Input Device implementation for Conjure Arcade Controllers +- Arcade stick support with directional buttons (up, down, left, right) +- 8 button support (Home, Start, 1-3, A-C) +- Multi-controller support with controller indexing +- Integration with Unity's Input System +- Input Action Asset extensions for easy controller assignment + +## Requirements + +- Unity 2021.3 or later +- Unity Input System package (1.4.4 or later) + +## Installation + +### Via Package Manager (Git URL) +1. Open the Package Manager in Unity +2. Click the "+" button and select "Add package from git URL" +3. Enter the repository URL + +### Via Package Manager (Local) +1. Download or clone this repository +2. Open the Package Manager in Unity +3. Click the "+" button and select "Add package from disk" +4. Select the `package.json` file from the downloaded folder + +## Quick Start + +### Basic Usage + +```csharp +using ConjureOS.Input; +using UnityEngine; + +public class PlayerController : MonoBehaviour +{ + void Update() + { + var controller = ConjureArcadeController.current; + if (controller == null) return; + + // Read stick input + Vector2 stickInput = controller.stick.ReadValue(); + + // Check button presses + if (controller.buttonA.wasPressedThisFrame) + { + // Handle A button press + } + + if (controller.start.wasPressedThisFrame) + { + // Handle start button press + } + } +} +``` + +### Using Input Actions + +1. Create an Input Action Asset in your project +2. Add actions and bind them to Conjure controller inputs: + - Stick: `/stick` + - Buttons: `/buttonA`, `/start`, etc. + +```csharp +using ConjureOS.Input; +using UnityEngine; +using UnityEngine.InputSystem; + +public class InputActionExample : MonoBehaviour +{ + [SerializeField] private InputActionAsset inputActions; + + void Start() + { + // Assign controller 0 to this input action asset + inputActions.AssignConjureController(0); + inputActions.Enable(); + } +} +``` + +### Multi-Controller Support + +```csharp +// Check how many controllers are connected +int controllerCount = ConjureArcadeController.ControllerCount; + +// Get specific controller by index +ConjureArcadeController player1 = ConjureArcadeController.GetForIndex(0); +ConjureArcadeController player2 = ConjureArcadeController.GetForIndex(1); + +// Check if controller exists for index +if (ConjureArcadeController.ExistForIndex(0)) +{ + // Controller 0 is available +} +``` + +## API Reference + +### ConjureArcadeController + +Main controller class that provides access to all inputs. + +**Properties:** +- `stick` - ConjureArcadeStickControl for joystick input +- `home`, `start` - ButtonControl for system buttons +- `button1`, `button2`, `button3` - ButtonControl for numbered buttons +- `buttonA`, `buttonB`, `buttonC` - ButtonControl for lettered buttons +- `ControllerIndex` - The index of this controller instance + +**Static Properties:** +- `current` - Currently active controller +- `allControllers` - Array of all connected controllers +- `ControllerCount` - Number of connected controllers + +**Static Methods:** +- `ExistForIndex(int)` - Check if controller exists for index +- `GetForIndex(int)` - Get controller by index + +**Events:** +- `OnControllerAdded` - Fired when a controller is connected +- `OnControllerRemoved` - Fired when a controller is disconnected + +### ConjureArcadeStickControl + +Custom stick control that provides both Vector2 input and directional buttons. + +**Properties:** +- `up`, `down`, `left`, `right` - ButtonControl for directional input +- Standard Vector2Control properties for analog input + +### InputActionAssetExtension + +Extension methods for InputActionAsset to simplify controller assignment. + +**Methods:** +- `AssignConjureController(int, bool)` - Assign specific controller to asset +- `CanAssignConjureController(int)` - Check if controller can be assigned +- `GetConjureControllerIndex()` - Get assigned controller index +- `GetConjureControllerIndexes()` - Get all assigned controller indexes + +## Input Bindings + +When setting up Input Actions, use these binding paths: + +- **Stick (Vector2)**: `/stick` +- **Stick Directions**: + - `/stick/up` + - `/stick/down` + - `/stick/left` + - `/stick/right` +- **Buttons**: + - `/home` + - `/start` + - `/button1` + - `/button2` + - `/button3` + - `/buttonA` + - `/buttonB` + - `/buttonC` + +## Hardware Information + +The controller is detected as: +- **Interface**: HID +- **Product**: "Generic USB Joystick " (note the extra spaces) diff --git a/README.md.meta b/README.md.meta new file mode 100644 index 0000000..1579e21 --- /dev/null +++ b/README.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7f42a6a74cfb6af41bc1b91caf3bfc1b +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime.meta b/Runtime.meta new file mode 100644 index 0000000..018bc7d --- /dev/null +++ b/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d47fc24aa1724934197bbef7ba60988e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/ConjureArcadeController.cs b/Runtime/ConjureArcadeController.cs new file mode 100644 index 0000000..9595edf --- /dev/null +++ b/Runtime/ConjureArcadeController.cs @@ -0,0 +1,135 @@ +#if ENABLE_INPUT_SYSTEM +using System; +using System.Collections.Generic; +using UnityEngine.InputSystem; +using UnityEngine.InputSystem.Controls; +using UnityEngine.InputSystem.Layouts; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable ClassNeverInstantiated.Global +// Reason: It's normal that some stuff is not used here because this is meant to be used as a library. + +// ReSharper disable InconsistentNaming +// Reason: According to our code guidelines, property should be UpperCamelCase. +// However, to be consistent with other input devices in Unity (e.g. Joystick, Gamepad) we will use lowerCamelCase here. + +namespace ConjureOS.Input +{ + [InputControlLayout(stateType = typeof(ConjureArcadeControllerState), displayName = "Conjure Arcade Controller")] + public class ConjureArcadeController : InputDevice + { + public static event Action OnControllerAdded; + public static event Action OnControllerRemoved; + + public ConjureArcadeStickControl stick { get; protected set; } + + public ButtonControl home { get; protected set; } + public ButtonControl start { get; protected set; } + + public ButtonControl button1 { get; protected set; } + public ButtonControl button2 { get; protected set; } + public ButtonControl button3 { get; protected set; } + + public ButtonControl buttonA { get; protected set; } + public ButtonControl buttonB { get; protected set; } + public ButtonControl buttonC { get; protected set; } + + public static ConjureArcadeController current { get; private set; } + public static ConjureArcadeController[] allControllers => allInstances.ToArray(); + public static int ControllerCount => count; + + /// + /// The controller index of this specific Conjure Arcade Controller. + /// The value will be between 0 and - 1. + /// If the value is -1, it means this controller was not initialized correctly and is not valid. + /// + public int ControllerIndex + { + get + { + try + { + return allInstances.IndexOf(this); + } + catch (ArgumentOutOfRangeException) + { + return -1; + } + } + } + + /// + /// Whether or not a Conjure Arcade Controller exist for the following index. + /// + public static bool ExistForIndex(int controllerIndex) + { + return allInstances.Exists(instance => instance.ControllerIndex == controllerIndex); + } + + /// + /// Get the Conjure Arcade Controller associated with the specific controller index. + /// + /// The controller if it exist for the specific index or null if it does not exist. + public static ConjureArcadeController GetForIndex(int controllerIndex) + { + return allInstances.Find(instance => instance.ControllerIndex == controllerIndex); + } + + protected override void FinishSetup() + { + stick = GetChildControl("stick"); + + home = GetChildControl("home"); + start = GetChildControl("start"); + + button1 = GetChildControl("button1"); + button2 = GetChildControl("button2"); + button3 = GetChildControl("button3"); + + buttonA = GetChildControl("buttonA"); + buttonB = GetChildControl("buttonB"); + buttonC = GetChildControl("buttonC"); + + base.FinishSetup(); + } + + public override void MakeCurrent() + { + base.MakeCurrent(); + current = this; + } + + protected override void OnAdded() + { + base.OnAdded(); + + if (!allInstances.Contains(this)) + { + allInstances.Add(this); + ++count; + OnControllerAdded?.Invoke(this); + } + } + + protected override void OnRemoved() + { + if (current == this) + { + current = null; + } + + if (allInstances.Remove(this)) + { + --count; + OnControllerRemoved?.Invoke(this); + } + + base.OnRemoved(); + } + + private static int count; + private static readonly List allInstances = new(); + } +} +#endif \ No newline at end of file diff --git a/Runtime/ConjureArcadeController.cs.meta b/Runtime/ConjureArcadeController.cs.meta new file mode 100644 index 0000000..e29b5e6 --- /dev/null +++ b/Runtime/ConjureArcadeController.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 036811b2c9f17694498f695d3074d167 \ No newline at end of file diff --git a/Runtime/ConjureArcadeControllerInfo.cs b/Runtime/ConjureArcadeControllerInfo.cs new file mode 100644 index 0000000..d55fc06 --- /dev/null +++ b/Runtime/ConjureArcadeControllerInfo.cs @@ -0,0 +1,31 @@ +namespace ConjureOS.Input +{ + public static class ConjureArcadeControllerInfo + { + // The information in this class will need to change if we change the way the controller is made. + // Everything in this class needs to be static since it is used at compile time to setup the Conjure Arcade Controller state description. + + public const string Interface = "HID"; + public const string Product = "Generic USB Joystick "; // The extra spaces are normal as they are part of the product name created by the board's vendor + + public const int StateSizeInBytes = 8; + public const int ReportIdByte = 0; + public const int StickXByte = 1; + public const int StickYByte = 1; + public const int ButtonByte = 7; + + public enum ButtonBit : uint + { + Home = 0, + Start = 1, + + One = 2, + Two = 3, + Three = 4, + + A = 5, + B = 6, + C = 7, + } + } +} \ No newline at end of file diff --git a/Runtime/ConjureArcadeControllerInfo.cs.meta b/Runtime/ConjureArcadeControllerInfo.cs.meta new file mode 100644 index 0000000..bbf8a92 --- /dev/null +++ b/Runtime/ConjureArcadeControllerInfo.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: d8c08e87547cb3a4a8471054f99c9442 \ No newline at end of file diff --git a/Runtime/ConjureArcadeControllerState.cs b/Runtime/ConjureArcadeControllerState.cs new file mode 100644 index 0000000..ed9c841 --- /dev/null +++ b/Runtime/ConjureArcadeControllerState.cs @@ -0,0 +1,53 @@ +#if ENABLE_INPUT_SYSTEM +using System.Runtime.InteropServices; +using UnityEngine.InputSystem.Layouts; +using UnityEngine.InputSystem.LowLevel; +using UnityEngine.InputSystem.Utilities; + +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable FieldCanBeMadeReadOnly.Global +// Reason: This is a configuration class with specific requirements for its interface. + +// ReSharper disable StringLiteralTypo +// ReSharper disable CommentTypo +// Reason: SHRT is not a typo in this case. + +// Inspired by: https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/manual/HID.html +// This class describes the data received by the controller in a way that can be read by the new input system. +// If the physical controller ever changes, this class will need to be reworked. + +namespace ConjureOS.Input +{ + [StructLayout(LayoutKind.Explicit, Size = ConjureArcadeControllerInfo.StateSizeInBytes)] + public struct ConjureArcadeControllerState : IInputStateTypeInfo + { + public FourCC format => new FourCC('H', 'I', 'D'); + + [FieldOffset(ConjureArcadeControllerInfo.ReportIdByte)] + public byte reportId; + + [InputControl(name = "stick", layout = "ConjureArcadeStick", format = "VC2B", displayName = "Stick", processors = "ConjureArcadeVector2(minX=0.0, maxX=1.0, minY=0.0, maxY=1.0, invertY)")] + [InputControl(name = "stick/x", offset = 0, format = "BYTE", parameters = "clamp=false, invert=false, normalize=false")] + [InputControl(name = "stick/left", offset = 0, format = "BYTE")] + [InputControl(name = "stick/right", offset = 0, format = "BYTE")] + [InputControl(name = "stick/y", offset = 1, format = "BYTE", parameters = "clamp=false, invert=false, normalize=false")] + [InputControl(name = "stick/up", offset = 1, format = "BYTE")] + [InputControl(name = "stick/down", offset = 1, format = "BYTE")] + [FieldOffset(ConjureArcadeControllerInfo.StickXByte)] + public byte stickX; + [FieldOffset(ConjureArcadeControllerInfo.StickYByte)] + public byte stickY; + + [InputControl(name = "home", layout = "Button", bit = (uint) ConjureArcadeControllerInfo.ButtonBit.Home, displayName = "Home")] + [InputControl(name = "start", layout = "Button", bit = (uint) ConjureArcadeControllerInfo.ButtonBit.Start, displayName = "Start")] + [InputControl(name = "button1", layout = "Button", bit = (uint) ConjureArcadeControllerInfo.ButtonBit.One, displayName = "Button 1", shortDisplayName = "1")] + [InputControl(name = "button2", layout = "Button", bit = (uint) ConjureArcadeControllerInfo.ButtonBit.Two, displayName = "Button 2", shortDisplayName = "2")] + [InputControl(name = "button3", layout = "Button", bit = (uint) ConjureArcadeControllerInfo.ButtonBit.Three, displayName = "Button 3", shortDisplayName = "3")] + [InputControl(name = "buttonA", layout = "Button", bit = (uint) ConjureArcadeControllerInfo.ButtonBit.A, displayName = "Button A", shortDisplayName = "A")] + [InputControl(name = "buttonB", layout = "Button", bit = (uint) ConjureArcadeControllerInfo.ButtonBit.B, displayName = "Button B", shortDisplayName = "B")] + [InputControl(name = "buttonC", layout = "Button", bit = (uint) ConjureArcadeControllerInfo.ButtonBit.C, displayName = "Button C", shortDisplayName = "C")] + [FieldOffset(ConjureArcadeControllerInfo.ButtonByte)] + public byte buttons; + } +} +#endif \ No newline at end of file diff --git a/Runtime/ConjureArcadeControllerState.cs.meta b/Runtime/ConjureArcadeControllerState.cs.meta new file mode 100644 index 0000000..e499157 --- /dev/null +++ b/Runtime/ConjureArcadeControllerState.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: afefdccae19d04943bdf5bb615f7325e \ No newline at end of file diff --git a/Runtime/ConjureArcadeStickControl.cs b/Runtime/ConjureArcadeStickControl.cs new file mode 100644 index 0000000..add4323 --- /dev/null +++ b/Runtime/ConjureArcadeStickControl.cs @@ -0,0 +1,46 @@ +#if ENABLE_INPUT_SYSTEM +using UnityEngine.InputSystem.Controls; +using UnityEngine.InputSystem.Layouts; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// Reason: It's normal that some stuff is not used here because this is meant to be used as a library. + +// ReSharper disable InconsistentNaming +// Reason: According to our code guidelines, property should be UpperCamelCase. +// However, to be consistent with other input controls in Unity (e.g. StickControl) we will use lowerCamelCase here. + +// This class exists to allow the Conjure arcade stick input to work with the input system. +// It uses the ConjureArcadeValue processor in order to define the down/left/right/up buttons. +// It is registered in when ConjureInputSystem is initialized and is used for the stick input in the controller state. + +namespace ConjureOS.Input +{ + public class ConjureArcadeStickControl : Vector2Control + { + [InputControl(useStateFrom = "y", processors = "ConjureArcadeValue(min=0.0, max=1.0, invert)", synthetic = true, displayName = "Up")] + [InputControl(name = "x", minValue = -1f, maxValue = 1f, layout = "Axis", processors = "ConjureArcadeValue(min=0.0, max=1.0)", format = "BYTE", sizeInBits = 8)] + [InputControl(name = "y", minValue = -1f, maxValue = 1f, layout = "Axis", processors = "ConjureArcadeValue(min=0.0, max=1.0, invert)", format ="BYTE", sizeInBits = 8, offset = 1)] + public ButtonControl up { get; set; } + + [InputControl(useStateFrom = "y" , processors = "ConjureArcadeValue(min=0.0, max=1.0)", synthetic = true, displayName = "Down")] + public ButtonControl down { get; set; } + + [InputControl(useStateFrom = "x", processors = "ConjureArcadeValue(min=0.0, max=1.0, invert)", synthetic = true, displayName = "Left")] + public ButtonControl left { get; set; } + + [InputControl(useStateFrom = "x", processors = "ConjureArcadeValue(min=0.0, max=1.0)", synthetic = true, displayName = "Right")] + public ButtonControl right { get; set; } + + protected override void FinishSetup() + { + base.FinishSetup(); + up = GetChildControl("up"); + down = GetChildControl("down"); + left = GetChildControl("left"); + right = GetChildControl("right"); + } + } +} +#endif \ No newline at end of file diff --git a/Runtime/ConjureArcadeStickControl.cs.meta b/Runtime/ConjureArcadeStickControl.cs.meta new file mode 100644 index 0000000..dcdf65d --- /dev/null +++ b/Runtime/ConjureArcadeStickControl.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: ab2d6e2ab1431cf4faf722a4c7b0e16d \ No newline at end of file diff --git a/Runtime/ConjureArcadeStickProcessor.cs b/Runtime/ConjureArcadeStickProcessor.cs new file mode 100644 index 0000000..0202c2c --- /dev/null +++ b/Runtime/ConjureArcadeStickProcessor.cs @@ -0,0 +1,79 @@ +#if ENABLE_INPUT_SYSTEM +using JetBrains.Annotations; +using UnityEngine; +using UnityEngine.InputSystem; + +// ReSharper disable ConvertToConstant.Local +// Reason: Attributes cannot be made into constants here since they are changed by Unity's internal input system. + +// Those classes exist to allow the conversion of the stick input into a normalized (-1.0, 1.0) range. +// They are registered when ConjureInputSystem is initialized and are used for stick inputs in the controller state and the stick control. + +namespace ConjureOS.Input +{ + [UsedImplicitly] + public class ConjureArcadeVector2Processor : InputProcessor + { + private readonly float minX = -1.0f; + private readonly float maxX = 1.0f; + private readonly bool invertX = false; + + private readonly float minY = -1.0f; + private readonly float maxY = 1.0f; + private readonly bool invertY = false; + + private readonly float deadZone = 0.1f; + + public override Vector2 Process(Vector2 value, InputControl control) + { + return new Vector2( + ConjureArcadeStickProcessorHelper.ProcessValue(value.x, minX, maxX, deadZone, invertX), + ConjureArcadeStickProcessorHelper.ProcessValue(value.y, minY, maxY, deadZone, invertY)); + } + } + + [UsedImplicitly] + public class ConjureArcadeValueProcessor : InputProcessor + { + private readonly float min = -1.0f; + private readonly float max = 1.0f; + private readonly bool invert = false; + + private readonly float deadZone = 0.1f; + + public override float Process(float value, InputControl control) + { + return ConjureArcadeStickProcessorHelper.ProcessValue(value, min, max, deadZone, invert); + } + } + + internal static class ConjureArcadeStickProcessorHelper + { + private const float MinStickValue = -1.0f; + private const float MaxStickValue = 1.0f; + private const float StickRange = MaxStickValue - MinStickValue; + + internal static float ProcessValue(float originalValue, float min, float max, float deadZone, bool invert) + { + float zero = (min + max) / 2; + if (Mathf.Approximately(originalValue, zero)) + { + return 0.0f; + } + + float originalRange = max - min; + if (Mathf.Approximately(originalRange, 0.0f)) + { + return 0.0f; + } + + float processedValue = (((originalValue - min) * StickRange) / originalRange) + MinStickValue; + processedValue = Mathf.Clamp(processedValue, MinStickValue, MaxStickValue); + processedValue = invert ? -processedValue : processedValue; + processedValue = processedValue > -deadZone && processedValue < deadZone ? 0.0f : processedValue; + + return processedValue; + } + } +} +#endif \ No newline at end of file diff --git a/Runtime/ConjureArcadeStickProcessor.cs.meta b/Runtime/ConjureArcadeStickProcessor.cs.meta new file mode 100644 index 0000000..d4121f4 --- /dev/null +++ b/Runtime/ConjureArcadeStickProcessor.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: eb4dcc44d20ac444ea46786d048561a3 \ No newline at end of file diff --git a/Runtime/ConjureInputSystem.cs b/Runtime/ConjureInputSystem.cs new file mode 100644 index 0000000..943bc01 --- /dev/null +++ b/Runtime/ConjureInputSystem.cs @@ -0,0 +1,38 @@ +using UnityEditor; +using UnityEngine; +#if ENABLE_INPUT_SYSTEM +using UnityEngine.InputSystem.Layouts; +using UnityEngine.InputSystem; +#endif + +namespace ConjureOS.Input +{ +#if UNITY_EDITOR + [InitializeOnLoad] +#endif + public static class ConjureInputSystem + { +#if UNITY_EDITOR + static ConjureInputSystem() + { + Initialize(); + } +#endif + + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSplashScreen)] + private static void Initialize() + { +#if ENABLE_INPUT_SYSTEM + InputSystem.RegisterProcessor(typeof(ConjureArcadeVector2Processor), "ConjureArcadeVector2"); + InputSystem.RegisterProcessor(typeof(ConjureArcadeValueProcessor), "ConjureArcadeValue"); + + InputSystem.RegisterLayout("ConjureArcadeStick"); + + InputSystem.RegisterLayout( + matches: new InputDeviceMatcher() + .WithInterface(ConjureArcadeControllerInfo.Interface) + .WithProduct(ConjureArcadeControllerInfo.Product)); +#endif + } + } +} diff --git a/Runtime/ConjureInputSystem.cs.meta b/Runtime/ConjureInputSystem.cs.meta new file mode 100644 index 0000000..df8203b --- /dev/null +++ b/Runtime/ConjureInputSystem.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: cb240260947f798488f8c7566ac4c85e \ No newline at end of file diff --git a/Runtime/ConjureOS.Input.asmdef b/Runtime/ConjureOS.Input.asmdef new file mode 100644 index 0000000..f0ba174 --- /dev/null +++ b/Runtime/ConjureOS.Input.asmdef @@ -0,0 +1,24 @@ +{ + "name": "ConjureOS.Input", + "rootNamespace": "ConjureOS.Input", + "references": [ + "Unity.InputSystem" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [ + "ENABLE_INPUT_SYSTEM" + ], + "versionDefines": [ + { + "name": "com.unity.inputsystem", + "expression": "1.0.0", + "define": "ENABLE_INPUT_SYSTEM" + } + ], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Runtime/ConjureOS.Input.asmdef.meta b/Runtime/ConjureOS.Input.asmdef.meta new file mode 100644 index 0000000..a2b54b9 --- /dev/null +++ b/Runtime/ConjureOS.Input.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 92470253e98b5064e9b025cb4b38e9c9 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/InputActionAssetExtension.cs b/Runtime/InputActionAssetExtension.cs new file mode 100644 index 0000000..ac6e33b --- /dev/null +++ b/Runtime/InputActionAssetExtension.cs @@ -0,0 +1,109 @@ +#if ENABLE_INPUT_SYSTEM +using System.Collections.Generic; +using System.Linq; +using UnityEngine.InputSystem; +using UnityEngine.InputSystem.Utilities; + +// ReSharper disable InvalidXmlDocComment +// Reason: We don't want to comment the "this" parameter in the extension functions. + +namespace ConjureOS.Input +{ + public static class InputActionAssetExtension + { + /// + /// Assign a specific Conjure Arcade Controller to this Input Action Asset. + /// All other Conjure Arcade Controllers will be disabled for this asset. + /// If the controller cannot be found for the controller index, there will be no assigned Conjure Arcade Controller. + /// + /// + /// If this is false, all devices will be disabled for this Input Action Asset except for the Conjure Arcade Controller associated to the given controller index. + /// If this is true, the devices that are not Conjure Arcade Controller (e.g. mouse/keyboard) will not be disabled. + /// + public static void AssignConjureController(this InputActionAsset inputActionAsset, int controllerIndex, bool keepOtherDevices = true) + { + List inputDevices = new List(); + if (keepOtherDevices) + { + if (inputActionAsset.devices == null) + { + inputDevices.AddRange(InputSystem.devices); + } + else + { + inputDevices = new List(inputActionAsset.devices); + } + + inputDevices.RemoveAll(inputDevice => inputDevice is ConjureArcadeController); + } + + ConjureArcadeController controller = ConjureArcadeController.GetForIndex(controllerIndex); + if (controller != null) + { + inputDevices.Add(controller); + } + + inputActionAsset.devices = inputDevices.ToArray(); + } + + /// + /// Whether or not it is possible to assign a specific controller index to this Input Action Asset. + /// + public static bool CanAssignConjureController(this InputActionAsset inputActionAsset, int controllerIndex) + { + return ConjureArcadeController.ExistForIndex(controllerIndex); + } + + /// + /// Get the controller index of the Conjure Arcade Controller associated with this Input Action Asset. + /// + /// + /// The controller index found. + /// If there was no Conjure Arcade Controller associated with this Input Action Asset, return false. + /// If there was multiple Conjure Arcade Controllers associated with this Input Action Asset, return the index of the first controller found. + /// + public static int GetConjureControllerIndex(this InputActionAsset inputActionAsset) + { + ConjureArcadeController[] controllers = GetConjureArcadeControllersFromDevices(inputActionAsset.devices); + if (controllers.Length == 0) + { + return -1; + } + + return controllers[0].ControllerIndex; + } + + /// + /// Get the controller index of all the Conjure Arcade Controllers associated with this Input Action Asset. + /// + /// + /// The controller indexes found. + /// If there was no Conjure Arcade Controller associated with this Input Action Asset, return an empty array. + /// + public static int[] GetConjureControllerIndexes(this InputActionAsset inputActionAsset) + { + ConjureArcadeController[] controllers = GetConjureArcadeControllersFromDevices(inputActionAsset.devices); + return controllers.Select(controller => controller.ControllerIndex).ToArray(); + } + + private static ConjureArcadeController[] GetConjureArcadeControllersFromDevices(ReadOnlyArray? devices) + { + if (devices == null) + { + return ConjureArcadeController.allControllers; + } + + List controllers = new List(); + foreach (InputDevice inputDevice in devices) + { + if (inputDevice is ConjureArcadeController device) + { + controllers.Add(device); + } + } + + return controllers.ToArray(); + } + } +} +#endif \ No newline at end of file diff --git a/Runtime/InputActionAssetExtension.cs.meta b/Runtime/InputActionAssetExtension.cs.meta new file mode 100644 index 0000000..a5747a6 --- /dev/null +++ b/Runtime/InputActionAssetExtension.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: bc3a0f8cabb44494e8be1efd5c3a73bf \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..d5b02d8 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "com.conjureos.input", + "version": "1.0.0", + "displayName": "Conjure Arcade Controller", + "description": "Unity Input System support for Conjure Arcade Controllers. Provides custom input device implementation for arcade-style controllers with joystick and button controls.", + "unity": "2021.3", + "unityRelease": "0f1", + "dependencies": { + "com.unity.inputsystem": "1.4.4" + }, + "keywords": [ + "input", + "controller", + "arcade", + "joystick", + "hid" + ], + "author": { + "name": "ConjureOS" + }, + "type": "library", + "hideInEditor": false +} \ No newline at end of file diff --git a/package.json.meta b/package.json.meta new file mode 100644 index 0000000..59baa04 --- /dev/null +++ b/package.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 30590bcda1dcd6342a558e59162358b8 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: