Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question Assigning an input action asset to a Player Control script.

Discussion in 'Input System' started by RichardEulitz, Sep 14, 2020.

  1. RichardEulitz

    RichardEulitz

    Joined:
    Apr 2, 2020
    Posts:
    2
    Hello,
    My problem is, that I don't know, how I can assign the input action asset, called "Controler" in this case, to the game object player/its script.

    This is the Player script:
    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.InputSystem;
    6.  
    7. public class PlayerControl : MonoBehaviour
    8. {
    9.     public Controler controls;
    10.  
    11.     void awake()
    12.     {
    13.         controls.Player.Move.performed += ctx => Move();
    14.     }
    15.    
    16.     void Move()
    17.     {
    18.         Debug.Log("Player wants to move");
    19.     }
    20.  
    21.     private void OnEnable()
    22.     {
    23.         controls.Enable();
    24.     }
    25.  
    26.     private void OnDisable()
    27.     {
    28.         controls.Disable();
    29.     }
    30.  
    31. }
    32.  


    This is the script automatically generated from the input action asset:
    Code (CSharp):
    1.  
    2. // GENERATED AUTOMATICALLY FROM 'Assets/Controler.inputactions'
    3.  
    4. using System;
    5. using System.Collections;
    6. using System.Collections.Generic;
    7. using UnityEngine.InputSystem;
    8. using UnityEngine.InputSystem.Utilities;
    9.  
    10. public class @Controler : IInputActionCollection, IDisposable
    11. {
    12.     public InputActionAsset asset { get; }
    13.     public @Controler()
    14.     {
    15.         asset = InputActionAsset.FromJson(@"{
    16.    ""name"": ""Controler"",
    17.    ""maps"": [
    18.        {
    19.            ""name"": ""Player"",
    20.            ""id"": ""732573cb-85f0-420c-a151-0ac0e11d16f8"",
    21.            ""actions"": [
    22.                {
    23.                    ""name"": ""Move"",
    24.                    ""type"": ""PassThrough"",
    25.                    ""id"": ""4782f878-d69a-4554-b232-6b314ee5e79a"",
    26.                    ""expectedControlType"": ""Vector2"",
    27.                    ""processors"": """",
    28.                    ""interactions"": """"
    29.                }
    30.            ],
    31.            ""bindings"": [
    32.                {
    33.                    ""name"": ""Axis"",
    34.                    ""id"": ""2e342f8d-00bb-4654-a6fd-3219975f18c5"",
    35.                    ""path"": ""2DVector"",
    36.                    ""interactions"": """",
    37.                    ""processors"": """",
    38.                    ""groups"": """",
    39.                    ""action"": ""Move"",
    40.                    ""isComposite"": true,
    41.                    ""isPartOfComposite"": false
    42.                },
    43.                {
    44.                    ""name"": ""up"",
    45.                    ""id"": ""6043f4fe-b046-4290-a4e7-1872a688facc"",
    46.                    ""path"": ""<Gamepad>/leftStick/up"",
    47.                    ""interactions"": """",
    48.                    ""processors"": """",
    49.                    ""groups"": """",
    50.                    ""action"": ""Move"",
    51.                    ""isComposite"": false,
    52.                    ""isPartOfComposite"": true
    53.                },
    54.                {
    55.                    ""name"": ""down"",
    56.                    ""id"": ""ffc33f9e-47ad-4a29-9ab8-5101f323b22f"",
    57.                    ""path"": ""<Gamepad>/leftStick/down"",
    58.                    ""interactions"": """",
    59.                    ""processors"": """",
    60.                    ""groups"": """",
    61.                    ""action"": ""Move"",
    62.                    ""isComposite"": false,
    63.                    ""isPartOfComposite"": true
    64.                },
    65.                {
    66.                    ""name"": ""left"",
    67.                    ""id"": ""06b7d20c-32e0-4c8d-873b-3fe9d1143d89"",
    68.                    ""path"": ""<Gamepad>/leftStick/left"",
    69.                    ""interactions"": """",
    70.                    ""processors"": """",
    71.                    ""groups"": """",
    72.                    ""action"": ""Move"",
    73.                    ""isComposite"": false,
    74.                    ""isPartOfComposite"": true
    75.                },
    76.                {
    77.                    ""name"": ""right"",
    78.                    ""id"": ""052f6b4f-b4cd-4125-bfa5-43fffbe2484d"",
    79.                    ""path"": ""<Gamepad>/leftStick/right"",
    80.                    ""interactions"": """",
    81.                    ""processors"": """",
    82.                    ""groups"": """",
    83.                    ""action"": ""Move"",
    84.                    ""isComposite"": false,
    85.                    ""isPartOfComposite"": true
    86.                }
    87.            ]
    88.        }
    89.    ],
    90.    ""controlSchemes"": []
    91. }");
    92.         // Player
    93.         m_Player = asset.FindActionMap("Player", throwIfNotFound: true);
    94.         m_Player_Move = m_Player.FindAction("Move", throwIfNotFound: true);
    95.     }
    96.  
    97.     public void Dispose()
    98.     {
    99.         UnityEngine.Object.Destroy(asset);
    100.     }
    101.  
    102.     public InputBinding? bindingMask
    103.     {
    104.         get => asset.bindingMask;
    105.         set => asset.bindingMask = value;
    106.     }
    107.  
    108.     public ReadOnlyArray<InputDevice>? devices
    109.     {
    110.         get => asset.devices;
    111.         set => asset.devices = value;
    112.     }
    113.  
    114.     public ReadOnlyArray<InputControlScheme> controlSchemes => asset.controlSchemes;
    115.  
    116.     public bool Contains(InputAction action)
    117.     {
    118.         return asset.Contains(action);
    119.     }
    120.  
    121.     public IEnumerator<InputAction> GetEnumerator()
    122.     {
    123.         return asset.GetEnumerator();
    124.     }
    125.  
    126.     IEnumerator IEnumerable.GetEnumerator()
    127.     {
    128.         return GetEnumerator();
    129.     }
    130.  
    131.     public void Enable()
    132.     {
    133.         asset.Enable();
    134.     }
    135.  
    136.     public void Disable()
    137.     {
    138.         asset.Disable();
    139.     }
    140.  
    141.     // Player
    142.     private readonly InputActionMap m_Player;
    143.     private IPlayerActions m_PlayerActionsCallbackInterface;
    144.     private readonly InputAction m_Player_Move;
    145.     public struct PlayerActions
    146.     {
    147.         private @Controler m_Wrapper;
    148.         public PlayerActions(@Controler wrapper) { m_Wrapper = wrapper; }
    149.         public InputAction @Move => m_Wrapper.m_Player_Move;
    150.         public InputActionMap Get() { return m_Wrapper.m_Player; }
    151.         public void Enable() { Get().Enable(); }
    152.         public void Disable() { Get().Disable(); }
    153.         public bool enabled => Get().enabled;
    154.         public static implicit operator InputActionMap(PlayerActions set) { return set.Get(); }
    155.         public void SetCallbacks(IPlayerActions instance)
    156.         {
    157.             if (m_Wrapper.m_PlayerActionsCallbackInterface != null)
    158.             {
    159.                 @Move.started -= m_Wrapper.m_PlayerActionsCallbackInterface.OnMove;
    160.                 @Move.performed -= m_Wrapper.m_PlayerActionsCallbackInterface.OnMove;
    161.                 @Move.canceled -= m_Wrapper.m_PlayerActionsCallbackInterface.OnMove;
    162.             }
    163.             m_Wrapper.m_PlayerActionsCallbackInterface = instance;
    164.             if (instance != null)
    165.             {
    166.                 @Move.started += instance.OnMove;
    167.                 @Move.performed += instance.OnMove;
    168.                 @Move.canceled += instance.OnMove;
    169.             }
    170.         }
    171.     }
    172.     public PlayerActions @Player => new PlayerActions(this);
    173.     public interface IPlayerActions
    174.     {
    175.         void OnMove(InputAction.CallbackContext context);
    176.     }
    177. }
    178.  

    In unity, there should be the possibility to assign the input action asset, but there isn't.
    Unbenannt.JPG
    Please Help!
     
  2. djweaver

    djweaver

    Joined:
    Jul 4, 2020
    Posts:
    105
    You have to instantiate the auto-generated class with the new keyword in your Awake(). Keep in mind there are other ways to access the input actions.


    Player Input Component Method:

    The easiest way that I've found is by using the Player Input component, because you do not need the auto-generated class script for this. You just need the Input Action asset and the Player Input component.

    1. Create your Input Action Asset in the editor, and configure your action map and the appropriate actions and their keybindings. Click "save asset" (again, you don't need to worry about auto-generating the class here, you're just concerned with the asset)

    2. Create your player object, or whatever object you're going to be using to execute your input action events

    3. On this object, create a Player Input component. Also create the script you will use to process your actions and drop it onto this player.

    4. In the inspector for the Player Input component, there will be a field for a reference to your Input Action Asset. Drag the asset there. The field is named "Actions" and once you connect your Input Action Asset, you can choose your action map in the drop down menu below.

    5. While still in the Player Input component section in the inspector, find the drop-down menu that says "Behavior" and choose "Invoke Unity Events".

    6. Now create a method within the script linked to this object, and say, if your event is called Shoot, or Move, create a function with the prefix "On" + that name. Make sure to make it public void, and that it has an argument of type "InputAction.CallbackContext" or you will get an error. Here is an example for an action named Move:
    Code (CSharp):
    1.     public class PlayerController : MonoBehaviour
    2.     {
    3.         public void OnMove(InputAction.CallbackContext context)
    4.         {
    5.             if (context.started) //code for when action starts (key down)
    6.             else if (context.performed) //code for when action is executed
    7.             else if (context.canceled) //code for when action is completed or stopped (key up)
    8.         }
    9.  

    7. Now, go BACK to the Player Input inspector panel. Click the arrow to expand the "Events" section, then click the arrow to expand another section that is named after your selected action map (chosen in the drop down in step 4)

    8. You should see a list of action event panels below that correspond to each action (keybinding) you have in your Input Action Asset. To use one of your events in code, click the '+' icon to add a reference to the event. You will then need to drag the player object that contains the script you wish to use to process that event into the reference field on the left. (The one undernead the drop down menu with the three options "Off", "Runtime and Editor" and "Runtime Only"). Drag the player object to that reference field, and select "Runtime Only" from the dropdown menu.

    9. Now, from the dropdown menu on the right (in that same event panel) you should see a list of components associated with your linked player object. Find the one that is your script, and from there, you should find an option to choose the method you created in that script (in step 6)​


    9 steps seems like a lot, but it is actually fairly simple once you do it once or twice.


    Using the Auto-Generated Class Interface:

    This is an example of how to use the interface that is created by the auto-generated Input Action asset class. (The interface part is more towards the end specifically, but watch the entire video for context, because it is important)

     
    Last edited: Sep 14, 2020
  3. RichardEulitz

    RichardEulitz

    Joined:
    Apr 2, 2020
    Posts:
    2
    Awesome! It works now as intended... Thanks a lot!
     
    djweaver likes this.
  4. Oneiros90

    Oneiros90

    Joined:
    Apr 29, 2014
    Posts:
    78
    I don't understand why it is not possible to instantiate the autogenerated class using the
    InputActionAsset
    in the project. I don't want to have two instances of that asset (one in the project and one generated at runtime from hardcoded json), this creates a problem: if you expand the
    InputActionAsset
    you can see that actions have their own embedded asset and those are very useful to be serialized in components via drag and drop (the type is
    InputActionReference
    ). But those cannot be used correctly! Because they wrap an
    InputAction
    that IS NOT the one you would expect, since it comes from the project's asset and not from the instance (that at this point you're forced to put in an ugly singleton to pass it around). So if you, for example, disable the action, the other will remain active.

    The only solution I've found to still use serialized references is through an extension method that finds the correct action in the singleton:
    Code (CSharp):
    1. public static InputAction GetRuntimeInputAction(this InputActionReference reference)
    2. {
    3.     var instance = MyUglySingleton.Instance;
    4.     return instance.Inputs.FindAction(reference.action.id.ToString());
    5. }
    Am I doing it wrong? I don't understand why it just can't be serialized anymore (as it is shown in the video posted, which is obsolete at this point)