Search Unity

Question Detecting an HID device as an SNES Controller

Discussion in 'Input System' started by Usagi-Murasaki, Dec 8, 2023.

  1. Usagi-Murasaki

    Usagi-Murasaki

    Joined:
    Nov 5, 2014
    Posts:
    2
    Hello,

    I would like to allow players to use their SNES usb controller, it was detected with the old input system.
    But the new input system doesn't detect it, so I'm trying to create the scripts to enable it but I'm quite lost on what to do.

    First of all, I can see my controller in the "Unsuported" device list from the input debugging :

    upload_2023-12-8_18-15-2.png

    I've created a class that inherits from InputDevice by following this tutorial :


    SnesController.cs

    Code (CSharp):
    1. using System.Linq;
    2. using System.Runtime.InteropServices;
    3. using UnityEditor;
    4. using UnityEngine;
    5. using UnityEngine.InputSystem;
    6. using UnityEngine.InputSystem.Controls;
    7. using UnityEngine.InputSystem.Layouts;
    8. using UnityEngine.InputSystem.LowLevel;
    9. using UnityEngine.InputSystem.Utilities;
    10.  
    11. [StructLayout(LayoutKind.Explicit, Size = 13)]
    12. public struct SnesControllerState : IInputStateTypeInfo
    13. {
    14.   public FourCC format => new FourCC('H', 'I', 'D');
    15.  
    16.   [InputControl(name = "dpad", layout = "Dpad", usage = "Hatswitch", displayName = "D-Pad", format = "BIT", sizeInBits = 4, bit = 0)]
    17.   [InputControl(name = "dpad/up", layout = "Button", sizeInBits = 1, bit = (uint)GamepadButton.DpadUp)]
    18.   [InputControl(name = "dpad/down", layout = "Button", sizeInBits = 1, bit = (uint)GamepadButton.DpadDown)]
    19.   [InputControl(name = "dpad/left", layout = "Button", sizeInBits = 1, bit = (uint)GamepadButton.DpadLeft)]
    20.   [InputControl(name = "dpad/right", layout = "Button", sizeInBits = 1, bit = (uint)GamepadButton.DpadRight)]
    21.  
    22.   [InputControl(name = "buttonSouth", layout = "Button", bit = (uint)GamepadButton.South, usages = new[] { "Back", "Cancel" }, displayName = "B", shortDisplayName = "B")]
    23.   [InputControl(name = "buttonWest", layout = "Button", bit = (uint)GamepadButton.West, usages = new[] { "PrimaryAction", "Submit" }, displayName = "A", shortDisplayName = "A")]
    24.   [InputControl(name = "buttonNorth", layout = "Button", bit = (uint)GamepadButton.North, usage = "SecondaryAction",  displayName = "X", shortDisplayName = "X")]
    25.   [InputControl(name = "buttonEast", layout = "Button", bit = (uint)GamepadButton.East, displayName = "Y", shortDisplayName = "Y")]
    26.  
    27.   [InputControl(name = "leftShoulder", layout = "Button", bit = (uint)GamepadButton.LeftShoulder, displayName = "Left Shoulder", shortDisplayName = "LB")]
    28.   [InputControl(name = "rightShoulder", layout = "Button", bit = (uint)GamepadButton.RightShoulder, displayName = "Right Shoulder", shortDisplayName = "RB")]
    29.  
    30.   [InputControl(name = "start", layout = "Button", bit = (uint)GamepadButton.Start, usage = "Menu", displayName = "Start")]
    31.   [InputControl(name = "select", layout = "Button", bit = (uint)GamepadButton.Select, displayName = "Select")]
    32.   [FieldOffset(0)]
    33.   public uint buttons;
    34. }
    35.  
    36. [InputControlLayout(stateType = typeof(SnesControllerState))]
    37. #if UNITY_EDITOR
    38. [InitializeOnLoad]
    39. #endif
    40. public class SnesController : InputDevice
    41. {
    42.   public DpadControl dpad { get; private set; }
    43.  
    44.   public ButtonControl buttonSouth { get; private set; }
    45.   public ButtonControl buttonWest { get; private set; }
    46.   public ButtonControl buttonNorth { get; private set; }
    47.   public ButtonControl buttonEast { get; private set; }
    48.  
    49.   public ButtonControl leftShoulder { get; private set; }
    50.   public ButtonControl rightShoulder { get; private set; }
    51.  
    52.   public ButtonControl start { get; private set; }
    53.   public ButtonControl select { get; private set; }
    54.  
    55.   protected override void FinishSetup()
    56.   {
    57.     base.FinishSetup();
    58.  
    59.     dpad = GetChildControl<DpadControl>("dpad");
    60.    
    61.     buttonSouth = GetChildControl<ButtonControl>("buttonSouth");
    62.     buttonWest = GetChildControl<ButtonControl>("buttonWest");
    63.     buttonNorth = GetChildControl<ButtonControl>("buttonNorth");
    64.     buttonEast = GetChildControl<ButtonControl>("buttonWest");
    65.    
    66.     leftShoulder = GetChildControl<ButtonControl>("leftShoulder");
    67.     rightShoulder = GetChildControl<ButtonControl>("rightShoulder");
    68.    
    69.     start = GetChildControl<ButtonControl>("start");
    70.     select = GetChildControl<ButtonControl>("select");
    71.   }
    72.  
    73.   static SnesController()
    74.   {
    75.     InputSystem.RegisterLayout<SnesController>(
    76.       matches: new InputDeviceMatcher()
    77.         .WithInterface("HID")
    78.         .WithCapability("vendorId", 0x0810)
    79.         .WithCapability("productId", 0xE501)
    80.       );
    81.    
    82.     if(!InputSystem.devices.Any(x => x is SnesController)) {
    83.       InputSystem.AddDevice(new InputDeviceDescription
    84.       {
    85.         interfaceName = "HID",
    86.         capabilities = "{\"vendorId\":2064,\"productId\": 58625}"
    87.       });
    88.     }
    89.   }
    90.  
    91.   [RuntimeInitializeOnLoadMethod]
    92.   public static void Initialize()
    93.   {
    94.    
    95.   }
    96. }
    So I have a device called SnesController in the device list, but it doesn't seem to be connected to my controller, what am I missing?


    upload_2023-12-8_18-14-49.png


    Here's the json description of this device:

    Code (JavaScript):
    1. {
    2.     "interface": "HID",
    3.     "type": "",
    4.     "product": "usb gamepad           ",
    5.     "serial": "",
    6.     "version": "262",
    7.     "manufacturer": "",
    8.     "capabilities": {
    9.         "vendorId": 2064,
    10.         "productId": 58625,
    11.         "usage": 4,
    12.         "usagePage": 1,
    13.         "inputReportSize": 8,
    14.         "outputReportSize": 0,
    15.         "featureReportSize": 0,
    16.         "elements": [
    17.             {
    18.                 "usage": 1,
    19.                 "usagePage": 9,
    20.                 "unit": 0,
    21.                 "unitExponent": 0,
    22.                 "logicalMin": 0,
    23.                 "logicalMax": 0,
    24.                 "physicalMin": 0,
    25.                 "physicalMax": 0,
    26.                 "collectionIndex": 0,
    27.                 "reportType": 1,
    28.                 "reportId": 1,
    29.                 "reportCount": 1,
    30.                 "reportSizeInBits": 1,
    31.                 "reportOffsetInBits": 44,
    32.                 "flags": 2
    33.             },
    34.             {
    35.                 "usage": 2,
    36.                 "usagePage": 9,
    37.                 "unit": 0,
    38.                 "unitExponent": 0,
    39.                 "logicalMin": 0,
    40.                 "logicalMax": 0,
    41.                 "physicalMin": 0,
    42.                 "physicalMax": 0,
    43.                 "collectionIndex": 0,
    44.                 "reportType": 1,
    45.                 "reportId": 1,
    46.                 "reportCount": 1,
    47.                 "reportSizeInBits": 1,
    48.                 "reportOffsetInBits": 45,
    49.                 "flags": 2
    50.             },
    51.             {
    52.                 "usage": 3,
    53.                 "usagePage": 9,
    54.                 "unit": 0,
    55.                 "unitExponent": 0,
    56.                 "logicalMin": 0,
    57.                 "logicalMax": 0,
    58.                 "physicalMin": 0,
    59.                 "physicalMax": 0,
    60.                 "collectionIndex": 0,
    61.                 "reportType": 1,
    62.                 "reportId": 1,
    63.                 "reportCount": 1,
    64.                 "reportSizeInBits": 1,
    65.                 "reportOffsetInBits": 46,
    66.                 "flags": 2
    67.             },
    68.             {
    69.                 "usage": 4,
    70.                 "usagePage": 9,
    71.                 "unit": 0,
    72.                 "unitExponent": 0,
    73.                 "logicalMin": 0,
    74.                 "logicalMax": 0,
    75.                 "physicalMin": 0,
    76.                 "physicalMax": 0,
    77.                 "collectionIndex": 0,
    78.                 "reportType": 1,
    79.                 "reportId": 1,
    80.                 "reportCount": 1,
    81.                 "reportSizeInBits": 1,
    82.                 "reportOffsetInBits": 47,
    83.                 "flags": 2
    84.             },
    85.             {
    86.                 "usage": 5,
    87.                 "usagePage": 9,
    88.                 "unit": 0,
    89.                 "unitExponent": 0,
    90.                 "logicalMin": 0,
    91.                 "logicalMax": 0,
    92.                 "physicalMin": 0,
    93.                 "physicalMax": 0,
    94.                 "collectionIndex": 0,
    95.                 "reportType": 1,
    96.                 "reportId": 1,
    97.                 "reportCount": 1,
    98.                 "reportSizeInBits": 1,
    99.                 "reportOffsetInBits": 48,
    100.                 "flags": 2
    101.             },
    102.             {
    103.                 "usage": 6,
    104.                 "usagePage": 9,
    105.                 "unit": 0,
    106.                 "unitExponent": 0,
    107.                 "logicalMin": 0,
    108.                 "logicalMax": 0,
    109.                 "physicalMin": 0,
    110.                 "physicalMax": 0,
    111.                 "collectionIndex": 0,
    112.                 "reportType": 1,
    113.                 "reportId": 1,
    114.                 "reportCount": 1,
    115.                 "reportSizeInBits": 1,
    116.                 "reportOffsetInBits": 49,
    117.                 "flags": 2
    118.             },
    119.             {
    120.                 "usage": 7,
    121.                 "usagePage": 9,
    122.                 "unit": 0,
    123.                 "unitExponent": 0,
    124.                 "logicalMin": 0,
    125.                 "logicalMax": 0,
    126.                 "physicalMin": 0,
    127.                 "physicalMax": 0,
    128.                 "collectionIndex": 0,
    129.                 "reportType": 1,
    130.                 "reportId": 1,
    131.                 "reportCount": 1,
    132.                 "reportSizeInBits": 1,
    133.                 "reportOffsetInBits": 50,
    134.                 "flags": 2
    135.             },
    136.             {
    137.                 "usage": 8,
    138.                 "usagePage": 9,
    139.                 "unit": 0,
    140.                 "unitExponent": 0,
    141.                 "logicalMin": 0,
    142.                 "logicalMax": 0,
    143.                 "physicalMin": 0,
    144.                 "physicalMax": 0,
    145.                 "collectionIndex": 0,
    146.                 "reportType": 1,
    147.                 "reportId": 1,
    148.                 "reportCount": 1,
    149.                 "reportSizeInBits": 1,
    150.                 "reportOffsetInBits": 51,
    151.                 "flags": 2
    152.             },
    153.             {
    154.                 "usage": 9,
    155.                 "usagePage": 9,
    156.                 "unit": 0,
    157.                 "unitExponent": 0,
    158.                 "logicalMin": 0,
    159.                 "logicalMax": 0,
    160.                 "physicalMin": 0,
    161.                 "physicalMax": 0,
    162.                 "collectionIndex": 0,
    163.                 "reportType": 1,
    164.                 "reportId": 1,
    165.                 "reportCount": 1,
    166.                 "reportSizeInBits": 1,
    167.                 "reportOffsetInBits": 52,
    168.                 "flags": 2
    169.             },
    170.             {
    171.                 "usage": 10,
    172.                 "usagePage": 9,
    173.                 "unit": 0,
    174.                 "unitExponent": 0,
    175.                 "logicalMin": 0,
    176.                 "logicalMax": 0,
    177.                 "physicalMin": 0,
    178.                 "physicalMax": 0,
    179.                 "collectionIndex": 0,
    180.                 "reportType": 1,
    181.                 "reportId": 1,
    182.                 "reportCount": 1,
    183.                 "reportSizeInBits": 1,
    184.                 "reportOffsetInBits": 53,
    185.                 "flags": 2
    186.             },
    187.             {
    188.                 "usage": 1,
    189.                 "usagePage": 65280,
    190.                 "unit": 0,
    191.                 "unitExponent": 0,
    192.                 "logicalMin": 0,
    193.                 "logicalMax": 0,
    194.                 "physicalMin": 0,
    195.                 "physicalMax": 0,
    196.                 "collectionIndex": 0,
    197.                 "reportType": 1,
    198.                 "reportId": 1,
    199.                 "reportCount": 1,
    200.                 "reportSizeInBits": 1,
    201.                 "reportOffsetInBits": 56,
    202.                 "flags": 2
    203.             },
    204.             {
    205.                 "usage": 49,
    206.                 "usagePage": 1,
    207.                 "unit": 0,
    208.                 "unitExponent": 0,
    209.                 "logicalMin": 0,
    210.                 "logicalMax": 255,
    211.                 "physicalMin": 0,
    212.                 "physicalMax": 255,
    213.                 "collectionIndex": 0,
    214.                 "reportType": 1,
    215.                 "reportId": 1,
    216.                 "reportCount": 1,
    217.                 "reportSizeInBits": 8,
    218.                 "reportOffsetInBits": 32,
    219.                 "flags": 2
    220.             },
    221.             {
    222.                 "usage": 48,
    223.                 "usagePage": 1,
    224.                 "unit": 0,
    225.                 "unitExponent": 0,
    226.                 "logicalMin": 0,
    227.                 "logicalMax": 255,
    228.                 "physicalMin": 0,
    229.                 "physicalMax": 255,
    230.                 "collectionIndex": 0,
    231.                 "reportType": 1,
    232.                 "reportId": 1,
    233.                 "reportCount": 1,
    234.                 "reportSizeInBits": 8,
    235.                 "reportOffsetInBits": 24,
    236.                 "flags": 2
    237.             }
    238.         ],
    239.         "collections": []
    240.     }
    241. }