Search Unity

How to rebind composite actions?

Discussion in 'Input System' started by Bandit358, Oct 1, 2019.

  1. Bandit358

    Bandit358

    Joined:
    Jan 10, 2016
    Posts:
    1
    I have a composite action for movement



    And I want to have a button in game for each of those. So that when it's clicked it would start listening for a button to rebind it.
    Standard stuff, press button for 'Forward' and change it from 'W' to 'E'

    As far as I got is I have a button that has an OnClick function that passes the name of the action to rebind, finds it, and preforms the interactive rebind. Which works for regular actions, but how can I rebind a single key in a composite action?

    Code (CSharp):
    1.  
    2. public void RebindButton(string actionName)
    3. {
    4.     InputAction action = control.currentActionMap.asset.FindAction(actionName);
    5.  
    6.     control.currentActionMap.Disable();
    7.        
    8.     //start binding
    9.     var rebind = action.PerformInteractiveRebinding();
    10.     rebind.WithControlsExcluding("Mouse");
    11.     rebind.Start()
    12.           .OnComplete(finishedRebind)
    13.           .OnCancel(finishedRebind);
    14.  
    15. }
     
  2. JamhammerRob

    JamhammerRob

    Joined:
    Jan 15, 2016
    Posts:
    13
    Looking for the same solution.
     
  3. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    ATM the API makes this awkward. I'll go and have a look how to make that easier.

    Using WithTargetBinding you can instruct RebindingOperation to rebind a specific binding on an action. This can be used to direct it at specific part bindings.

    Code (CSharp):
    1. var bindingIndex = action.bindings.IndexOf(x => x.isPartOfComposite && x.name == "Up");
    2. rebind.WithTargetBinding(bindingIndex);
     
  4. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    Just to add this, while not perfect, there is an alternate way doable as of 1.1-preview.3 (though I think it's been in there for a bit).

    Code (CSharp):
    1. // Let's say you have a composite that you named
    2. // "WASD" in the editor. The following call returns
    3. // a sort of "iterator" which can also be used to
    4. // query binding indices.
    5. var wasd = action.ChangeBinding("WASD");
    6.  
    7. // We can use NextPartBinding() to iterate forward
    8. // to parts of the composite.
    9. var up = wasd.NextPartBinding("Up");
    10. var down = wasd.NextPartBinding("Down");
    11. var left = wasd.NextPartBinding("Left");
    12. var right = wasd.NextPartBinding("Right");
    13.  
    14. // And then we can use the bindingIndex property
    15. // to pass it on to the rebind.
    16. rebind.WithTargetBinding(up.bindingIndex);
     
  5. Transarc

    Transarc

    Joined:
    Sep 19, 2020
    Posts:
    6
    I'm on the latest version of the input system and I'm trying to implement the WASD rebinding. The action.ChangeBinding() doesn't accept an argument of type string and the wasd.NextPartBinding("") doesn't exist. How Can I Fix this ?
    upload_2021-3-7_15-44-43.png
     
  6. Transarc

    Transarc

    Joined:
    Sep 19, 2020
    Posts:
    6
    upload_2021-3-8_8-59-4.png
     

    Attached Files:

  7. Transarc

    Transarc

    Joined:
    Sep 19, 2020
    Posts:
    6
    Yes I tried but I'm a bit loss. After 5 hours it still didn't work at all. certain methods doesn't exist how can I declare them ? Can you guide me ?
     
  8. Post your (entire) script here, in CODE tags (see the post editor toolbar). Will take a look.
     
  9. Transarc

    Transarc

    Joined:
    Sep 19, 2020
    Posts:
    6
    My script work fine for normal rebinding but i failed to get a single thing that works for composite bindings like axis. So I deleted all the composite bindings code 2 days ago. I also tried to do what was said on this post. but as shown on the screen I sent above, the methods don't seem to be declared. How would you have done it? I think it might be of interest to a lot of people other than me (+ me).
     
    Last edited: Mar 12, 2021
  10. Santhura

    Santhura

    Joined:
    Jun 5, 2018
    Posts:
    1
    Hello Transarc,

    You are using the wrong method you should use ChangeCompositeBinding("Wasd"); instead then it does have that method.

    What I only have now is, when I try it out it doesn't register my input. I have a button and when I press it it does the code below and wait for a input respond, and for normal action buttons works fine, but for the composite does seems like it does nothing

    My code looks like this
    Code (CSharp):
    1.   _startRebindObject.SetActive(false);
    2.             _waitingForInputObject.SetActive(true);
    3.             _playerInput.SwitchCurrentActionMap("UI");
    4.  
    5.             var arrowKeys = _attackAction.action.ChangeCompositeBinding("ArrowKeys");
    6.  
    7.             var part = arrowKeys.NextPartBinding(partBinding);
    8.  
    9.             rebindingOperation = _attackAction.action.PerformInteractiveRebinding()
    10.                                                     .WithTargetBinding(part.bindingIndex)
    11.                                                     .WithControlsExcluding("Mouse")
    12.                                                     .OnMatchWaitForAnother(_WAITFORANOTHERINPUT_SECONDS)
    13.                                                     .OnComplete(operation => RebindComplete())
    14.                                                     .Start();
     
  11. TheodorGal1977

    TheodorGal1977

    Joined:
    Feb 3, 2021
    Posts:
    10
    Hello all. Im struggling with the same issues.

    I have a UI action map which has an action named "navigate". In there I have 2 binding groups (is that the correct term? not sure) one for the gamepad control scheme which listens to stick or d-pad inputs and one group for keyboard controlscheme. Inside that group I have four composites, Up,Left,Down and Right.

    I've tried nearly everything suggested in this thread and nothing seems to work.

    Code (CSharp):
    1. bindingIndex = navigateAction.action.bindings.IndexOf(x => x.name == composite);
    returns -1.

    I ended up with the following code but I know its junk (even though Im a beginner in Unity and scripting) even though it works. Currently I can this function by hooking it to buttons and pass the string parameter to "select" which rebind to perform.


    Code (CSharp):
    1.    public void StartRebindingWASD(string composite)
    2.     {
    3.         uicontrols.Disable();
    4.         switch (composite)
    5.         {
    6.          
    7. //  I ended up hardcoding the bindingIndex here, as nothing else seems to work
    8.             case "Up": selector = 0; bindingIndex = 11; break;
    9.             case "Left": selector = 1; bindingIndex = 12; break;
    10.             case "Down": selector = 2; bindingIndex = 13; break;
    11.             case "Right": selector = 3; bindingIndex = 14; break;
    12.             default: selector = -1; bindingIndex = -1; break;
    13.         }
    14.        
    15.  
    16.         wasdDisplayTexts[selector].text = "";
    17.         wasdStartRebindObject[selector].SetActive(false);
    18.         wasdWaitingForInputObject[selector].SetActive(true);
    19.  
    20.       // bindingIndex = navigateAction.action.bindings.IndexOf(x => x.name == composite);
    21.         Debug.Log(bindingIndex);
    22.         rebindingOperation = navigateAction.action.PerformInteractiveRebinding(bindingIndex)
    23.                                       .WithControlsExcluding("Mouse")
    24.                  // how the hell do I exclude the "enter" key for this rebinding?                    
    25.                                       .WithControlsExcluding("<Keyboard>/enter")
    26.                                       .WithCancelingThrough("<Keyboard>/escape")
    27.                                       .OnMatchWaitForAnother(0.1f)
    28.                                       .OnCancel(operation => RebindComplete(bindingIndex))
    29.                                       .OnComplete(operation => RebindComplete(bindingIndex))
    30.                                       .Start();
    31.     }
    32.  
    33.     void RebindComplete(int bindingIndex)
    34.     {
    35.         wasdDisplayTexts[selector].text = InputControlPath.ToHumanReadableString(navigateAction.action.bindings[bindingIndex].effectivePath, InputControlPath.HumanReadableStringOptions.OmitDevice);
    36.         rebindingOperation.Dispose();
    37.  
    38.         wasdStartRebindObject[selector].SetActive(true);
    39.         wasdWaitingForInputObject[selector].SetActive(false);
    40.         uicontrols.Enable();
    41.     }
    On a sidenote, how do I exclude the "enter" key? the above gives me "any key" when I hit the enter key when waiting for input.
     
  12. TheodorGal1977

    TheodorGal1977

    Joined:
    Feb 3, 2021
    Posts:
    10
    When I try to ise ChangeCompositeBinding it seems there is a namespace Im missing. Do I need a directive In unaware of?
     
  13. TheodorGal1977

    TheodorGal1977

    Joined:
    Feb 3, 2021
    Posts:
    10
    Ok for anyone else reading this.......

    Im using the 1.0.2 version so that explains why I cant use some of the above suggestions.

    As for excluding the "enter" key, or any other key for that matter, the Input debugger screen helped me realize what keys to exclude in my rebinding script to make it work as desired. So for example I noticed that "enter" key also registers a "anyKey" press so I also had to exclude that. Another example are the "shift" keys where you need to exclude "rightShift", "leftShift" and "shift" in order to exclude them from rebinding.
     
  14. Transarc

    Transarc

    Joined:
    Sep 19, 2020
    Posts:
    6
    I managed to make the system work a week ago. I can send the code if needed but I pretty much used the first solution of the thread.
     
    Last edited: Apr 12, 2021
    TheodorGal1977 likes this.
  15. MiguelCurwen

    MiguelCurwen

    Joined:
    Dec 11, 2018
    Posts:
    3

    I managed to implement this solution but the names were all in lowercase, even when the composite binding was named "Up" in the asset.

    So any search with the strings .ToLower or ToUpper just in case should do the work.
     
    TheodorGal1977 likes this.
  16. slitherpunk

    slitherpunk

    Joined:
    Dec 14, 2017
    Posts:
    1
    I've been starting using this method, and it seemed I was on the right track, as the up, down, left, right bindingIndex gave me the right numbers, but it doesn't actually allow me to press any buttons when performing the interactive rebind. It only allows something like, mouse scroll, or gamepad sticks/dpad. And then once it accepts even those, I start getting errors. Does anyone know why I can't use any keys when rebinding a composite?
    code looks like this (for keyboard rebinding):
    Code (CSharp):
    1. rebindOperation = currentButtonRebinding.PerformInteractiveRebinding();
    2.                     rebindOperation.WithTargetBinding(currentBindingIndex);
    3.                     rebindOperation.WithControlsExcluding("<Gamepad>");
    4.                     rebindOperation.OnMatchWaitForAnother(0.1f);
    5.                     rebindOperation.OnComplete(operation => RebindCompleted());
    6.  
    7.                 rebindOperation.Start();
    and, as a side, i do have other action type buttons that rebinding keys DO work. it's just when trying to rebind new composite for some reason. nothing happens, it never goes onto that RebindCompleted unless I use the scrollwheel which causes errors.
     
  17. vaskar61

    vaskar61

    Joined:
    Sep 23, 2019
    Posts:
    2
    Hey, I solved the problem its really simple
    Ex:
    int index=1//it should start from 1 cuz 0 is for the actual composite wasd binding and then 1 for W subbinding,2 for S subbinding and so on as we move down the group.
    wasd.action.PerformInteractiveRebinding(index).WithCancelingThrough("<Keyboard>/escape").WithControlsExcluding("Mouse").OnCancel(Cancel).OnMatchWaitForAnother(.1f).OnComplete(Completed).Start();

    So if u r making a rebinding script,
    it should be like this,
    1:it should retreive the action reference and show for which binding index it should do rebinding.
    2:after getting the binding index it should check whether the binding is composite or not.
    3.if it is not composite then simple do the rebinding other wise it should show for which subbinding index it should perform the rebinding which is always greater than 0 and then do rebinding with that subbinding index.

    I hope it helps
     
  18. vaskar61

    vaskar61

    Joined:
    Sep 23, 2019
    Posts:
    2
    You should always feed an binding index to the function action.PerformInteractiveRebinding(index) otherwise it will not work correctly if the bindings for that action is greater than 1.
     
  19. smasson99

    smasson99

    Joined:
    Oct 1, 2017
    Posts:
    10
    Could we get an update from Unity here as of how we are supposed to acheive composite rebindind? Also how to display their text would be nice.
     
    Banaaani likes this.
  20. DribalZ

    DribalZ

    Joined:
    Mar 24, 2018
    Posts:
    2
    Hi, after many searches I think I'm able to help :oops:
    First I followed this tuto:

    But it doesn't explain what we are looking for here: binding composites (Ravcho gives clues in the comments)

    What I have (and work for both) is:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. using UnityEngine.UI;
    6. using UnityEngine.InputSystem;
    7. using UnityEngine.Events;
    8. using UnityEngine.EventSystems;
    9.  
    10. using Sirenix.OdinInspector;
    11.  
    12. public class InputRebinder : MonoBehaviour
    13. {
    14.  
    15.     InputActionRebindingExtensions.RebindingOperation RebindingOperation;
    16.     public InputActionAsset InputActionAsset;
    17.     InputAction InputAction;
    18.     PlayerInput PlayerInput;
    19.     public string MapName;
    20.     public string InputName;
    21.     [ShowIf("@type == Type.Composite")] [Range(0,10)] public int BindingIndex;
    22.     public Text Text;
    23.    
    24.     public enum Type
    25.      {
    26.         Button,
    27.         Composite,
    28.      }
    29.      public Type type;
    30.    
    31.     private void Awake()
    32.     {
    33.         InputAction = InputActionAsset.FindAction(MapName + "/" + InputName);
    34.         if (InputActionAsset == null | InputAction == null) { print("InputAction PB"); return;}
    35.     }
    36.    
    37.     //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
    38.     private void Start()
    39.     {
    40.         //Display the Binded key on the button at start
    41.         InputBindingText(InputAction);
    42.     }
    43.    
    44.     //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
    45.     private void Update()
    46.     {
    47.         if (TitleMenu.isPlayerCreated & PlayerInput == null)
    48.         {
    49.             PlayerInput = TitleMenu.player_prefab.GetComponent<PlayerInput>();
    50.         }
    51.        
    52.         //Update to see in game the Binded key on the button and adjust the correct value in this inspector
    53.         InputBindingText(InputAction);
    54.     }
    55.    
    56.     public void StartRebinding()
    57.     {  
    58.         PlayerInput.SwitchCurrentActionMap("UI");
    59.        
    60.         if (type == Type.Button)
    61.         {
    62.             RebindingOperation = InputAction.PerformInteractiveRebinding()
    63.                 .WithControlsExcluding("Mouse")
    64.                 .OnMatchWaitForAnother(0.1f)
    65.                 .OnComplete(operation => RebindComplete())
    66.                 .Start();
    67.         }
    68.         else
    69.         if (type == Type.Composite)
    70.         {
    71.                 RebindingOperation = InputAction.PerformInteractiveRebinding()
    72.                 .WithTargetBinding(BindingIndex)
    73.                 .OnMatchWaitForAnother(.1f)
    74.                 .OnComplete(operation => RebindComplete())
    75.                 .Start();
    76.         }
    77.     }
    78.  
    79.     private void RebindComplete()
    80.     {
    81.         InputBindingText(InputAction);
    82.  
    83.         RebindingOperation.Dispose();
    84.  
    85.         PlayerInput.SwitchCurrentActionMap(MapName);
    86.     }
    87.    
    88.     void InputBindingText(InputAction InputAction)
    89.     {
    90.         var bindingIndex = 0;
    91.        
    92.         if (type == Type.Button)
    93.             bindingIndex = InputAction.GetBindingIndexForControl(InputAction.controls[0]);
    94.         else
    95.         if (type == Type.Composite)
    96.             bindingIndex = BindingIndex;
    97.        
    98.         Text.text = InputControlPath.ToHumanReadableString(
    99.             InputAction.bindings[bindingIndex].effectivePath,
    100.             InputControlPath.HumanReadableStringOptions.OmitDevice);
    101.        
    102.     }
    103.    
    104.     //Menus
    105.     TitleMenu TitleMenu => Load.TitleMenu;//My personal logic, u don't need this (I use it to check if player's  been                                                                              instanciated)
    106. }
    107.  
    As Vaskar61 said, it's a pretty straight forward process just focus on InputAction.bindings you have in your InputAction, it's an array that contains all composites you created into your action. And surprisingly everything you see in there:

    upload_2023-6-5_18-47-8.png
    follows a straight numerotation from 0 to X (WASD is part of the array ! o_O I thought I shouldn't but it is)

    So I used this value to check my buttons key, using InputBindingText(InputAction InputAction) in my Udpate()

    Code (CSharp):
    1.    void InputBindingText(InputAction InputAction)
    2.     {
    3.         var bindingIndex = 0;
    4.        
    5.         if (type == Type.Button)
    6.             bindingIndex = InputAction.GetBindingIndexForControl(InputAction.controls[0]);
    7.         else
    8.         if (type == Type.Composite)
    9.             bindingIndex = BindingIndex;
    10.        
    11.         Text.text = InputControlPath.ToHumanReadableString(
    12.             InputAction.bindings[bindingIndex].effectivePath,
    13.             InputControlPath.HumanReadableStringOptions.OmitDevice);
    14.        
    15.     }
    upload_2023-6-5_18-52-20.png
    As I scroll the following, I can see in game the key Unity stored, this way I know wich index I should use for wich Button/Key:
    upload_2023-6-5_18-59-47.png

    NOTICE the OnClick Event on the button is calling Start Rebinding (coming from the same script and placed on the same GameObject, the Button. Each Button has it's own script to adjust the index for exemple)

    I Hope it helped :p
     

    Attached Files:

    ppukku999 likes this.
  21. baize97

    baize97

    Joined:
    Jun 23, 2020
    Posts:
    30
    hi,Do you know how to rebind a button to a key combination? For example, the default binding button of the Jump action is E, how to change it to Ctrl+E, inputSystem does not seem to support rebinding of type changes.