How do you map to KeyCode.JoystickButton0 in the new Unity Input System?

Viewed 4695

I'm working on a Unity project for Android TV and Fire TV that utilizes the new input system released in v2019.3.

Fire TV's button mappings in the old system were as follows:

Back                    KeyCode.Escape
Select (D-Pad Center)   KeyCode.JoystickButton0
Left (D-Pad)            KeyCode.LeftArrow
Right (D-Pad)           KeyCode.RightArrow
Up (D-Pad)              KeyCode.UpArrow
Down (D-Pad)            KeyCode.DownArrow

I've successfully mapped everything but Select/D-Pad Center with the following Binding Paths in the new system:

Escape [Keyboard]
Right Arrow [Keyboard]
Left Arrow [Keyboard]
Up Arrow [Keyboard]
Down Arrow [Keyboard]

If I map to Any Key [Keyboard] and implement the following code as the callback, it shows up as key: JoystickButton0, which matches Amazon's documentation, but doesn't exist as an option in the new system under keyboard binding paths:

public void DebugKey(InputAction.CallbackContext context)
{
    foreach(KeyCode vKey in System.Enum.GetValues(typeof(KeyCode))){
         if(Input.GetKey(vKey)){
             Debug.Log ("key: " + vKey);
         }
    }
}

I've tried various buttons under Gamepad, Android Gamepad, Joystick, and Android Joystick without any success. Another odd thing is that the Center/D-Pad works fine on UI Buttons without any binding.

What is the proper way to map to the legacy KeyCode.JoystickButtonX naming convention with the new input system?

Thanks!

2 Answers

In order to fully map the Amazon Fire Tv controller using Unity with the new input system you need a Custom device.

With this script you can get all the buttons work. It's also possible to get the volume up, volume down and Mute. But I don't think it's a good idea override this buttons.

using System.Linq;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.InputSystem.Utilities;
using UnityEngine.Scripting;
 
#if UNITY_EDITOR
using UnityEditor;
#endif
 
[Preserve]
public struct AftvRemoteDeviceState : IInputStateTypeInfo
{
    public FourCC format => new FourCC('A', 'G', 'C');
 
    //Mapped Here
    [InputControl(name = "dPadUpButton", layout = "Button", bit = 19, displayName = "Dpad Up")]
    [InputControl(name = "dPadRightButton", layout = "Button", bit = 22, displayName = "Dpad Right")]
    [InputControl(name = "dPadDownButton", layout = "Button", bit = 20, displayName = "Dpad Down")]
    [InputControl(name = "dPadLeftButton", layout = "Button", bit = 21, displayName = "Dpad Left")]
    //[InputControl(name = "backButton", layout = "Button", bit = 4, displayName = "Back Button")]
    [InputControl(name = "menuButton", layout = "Button", bit = 82, displayName = "Menu Button")]
    //Non Maped
    [InputControl(name = "selectButton", layout = "Button", bit = 23, displayName = "Select Button")]
    [InputControl(name = "rewindButton", layout = "Button", bit = 89, displayName = "Rewind Button")]
    [InputControl(name = "playPauseButton", layout = "Button", bit = 85, displayName = "Play Pause Button")]
    [InputControl(name = "fastForwardButton", layout = "Button", bit = 90, displayName = "Fast Forward Button")]
    public uint buttons;
}
 
#if UNITY_EDITOR
[InitializeOnLoad] // Call static class constructor in editor.
#endif
[Preserve]
[InputControlLayout(displayName = "Aftv Remote", stateType = typeof(AftvRemoteDeviceState))]
public class AftvRemoteDevice : InputDevice
{
#if UNITY_EDITOR
    static AftvRemoteDevice()
    {
        Initialize();
    }
#endif
 
    [RuntimeInitializeOnLoadMethod]
    private static void Initialize()
    {
        InputSystem.RegisterLayout<AftvRemoteDevice>
            (
                matches: new InputDeviceMatcher()
                .WithInterface("Android")
                .WithDeviceClass("AndroidGameController")
                .WithProduct("Amazon Fire TV Remote")
            );
    }
 
    public ButtonControl dPadUpButton { get; private set; }
    public ButtonControl dPadRightButton { get; private set; }
    public ButtonControl dPadDownButton { get; private set; }
    public ButtonControl dPadLeftButton { get; private set; }
    public ButtonControl selectButton { get; private set; }
    public ButtonControl rewindButton { get; private set; }
    public ButtonControl playPauseButton { get; private set; }
    public ButtonControl fastForwardButton { get; private set; }
    //public ButtonControl backButton { get; private set; }
    public ButtonControl menuButton { get; private set; }
    public bool SelectButtonDown { get; set; }
    public bool RewindButtonDown { get; set; }
    public bool PlayPauseButtonDown { get; set; }
    public bool FastForwardButtonDown { get; set; }
 
    protected override void FinishSetup()
    {
        base.FinishSetup();
        dPadUpButton = GetChildControl<ButtonControl>("dPadUpButton");
        dPadRightButton = GetChildControl<ButtonControl>("dPadRightButton");
        dPadDownButton = GetChildControl<ButtonControl>("dPadDownButton");
        dPadLeftButton = GetChildControl<ButtonControl>("dPadLeftButton");
 
        rewindButton = GetChildControl<ButtonControl>("rewindButton");
        playPauseButton = GetChildControl<ButtonControl>("playPauseButton");
        fastForwardButton = GetChildControl<ButtonControl>("fastForwardButton");
 
        //backButton = GetChildControl<ButtonControl>("backButton");
        menuButton = GetChildControl<ButtonControl>("menuButton");
        selectButton = GetChildControl<ButtonControl>("selectButton");    
    }
 
    public static AftvRemoteDevice current { get; private set; }
 
    public override void MakeCurrent()
    {
        base.MakeCurrent();
        current = this;
    }
 
    protected override void OnRemoved()
    {
        base.OnRemoved();
        if (current == this)
            current = null;
    }
 
    #region Editor
#if UNITY_EDITOR
    [MenuItem("Tools/AftvRemote/Create Device")]
    private static void CreateDevice()
    {
        InputSystem.AddDevice<AftvRemoteDevice>();
    }
    [MenuItem("Tools/AftvRemote/Remove Device")]
    private static void RemoveDevice()
    {
        var customDevice = InputSystem.devices.FirstOrDefault(x => x is AftvRemoteDevice);
        if (customDevice != null)
            InputSystem.RemoveDevice(customDevice);
    }
#endif
    #endregion
}

The bit values are the same Constant values from the Android API. You can check the list Here You only need to Include this script in the project setup your Input Actions and Input Action map.

For some reason the Player Input component have issues. But for UI navigation this works nice also it works fine with the Input Debugger.

In New system, the devices are read as HID, the KeyCode is not useful, so if you want to read the status of button of joystick, you could do that:

using UnityEngine.InputSystem;


public Joystick joy;

void Start()
{
     joy = Joystick.current;
}

void FixedUpdate()
{
    var button2 = joy.allControls[2].IsPressed();
    if (button2)
    {
        Debug.Log("Button2 of current joystick is pushed");
    }
}

If you want to map the button0 (in old inputsystem), its now the button1 or trigger

var button1 = joy.allControls[1].IsPressed();

or

var button1 = joy.trigger.IsPressed();

And you could test buttons of more sticks..

void Start()
{
    var ListOfJoys = Joystick.all;
     joy = Joystick.current;//here current joy is ListOfJoys[1]
     otherjoy = ListOfJoys[0];
}


    var Buttons = joy.allControls;


    if ((Buttons[2] as ButtonControl).wasPressedThisFrame)
    {
        Debug.Log("b2 pressed during this frame");
    }
    if ((Buttons[2] as ButtonControl).wasReleasedThisFrame)
    {
        Debug.Log("b2 released during this frame");
    }
    if (joy.trigger.wasPressedThisFrame)
    {
        Debug.Log("trig or button1 pressed during this frame");
    }
    if (joy.trigger.wasReleasedThisFrame)
    {
        Debug.Log("trig or button1 released during this fram");
    }
    if (otherjoy.trigger.wasPressedThisFrame)
    {
        Debug.Log("otherjoy trigger");
    }

the other way is to use the mapping of new system, i have mapped the action press Only Test1 to button3 of stick

enter image description here

that simplify the code and you could mix event and direct test:

//Newinputsystem is a name of class generated 
private Newinputsystem playerAction;

void Awake()
{
    playerAction = new Newinputsystem();
    playerAction.Player.Test1.performed += ctx => FireTest();
}

void FixedUpdate()
{
    if (playerAction.Player.Test1.triggered)
    {
        Debug.Log("fire!!!");
    }
}

public void FireTest()
{
    Debug.Log("i am in firetest");
}

private void OnEnable()
{
    playerAction.Enable();
}
private void OnDisable()
{
    playerAction.Disable();
}

so you could add new action test2 which will be triggered with action release only for button3...

TO display the HID device, go to menu Window/analysis/input debbugger

you have to see your device (here mine is hotas)

enter image description here

and you click on it and in the tab item HID Descriptor

enter image description here

so i suppose you have selected your device in project setting:

enter image description here

Related