PerformInteractiveRebindings for composite

Hello guys,

I want users to be able to rebind their controls through an UI, but I can’t find a way to do this with composite bindings :confused:

This is what my input asset looks like :

And this is basically what I’m trying to achieve :

public class InputsRebinding : MonoBehaviour
{

    [SerializeField] private TextMeshProUGUI forwardText;
    [SerializeField] private TextMeshProUGUI leftText;
    [SerializeField] private TextMeshProUGUI backwardText;
    [SerializeField] private TextMeshProUGUI rightText;

    [SerializeField] private Button forwardButton;
    [SerializeField] private Button leftButton;
    [SerializeField] private Button backwardButton;
    [SerializeField] private Button rightButton;

    [SerializeField] private InputActionReference movementReference;


    private void Awake()
    {
        UpdateText();
        forwardButton.onClick.AddListener(() => RemapAction(movementReference.action, 0));
        backwardButton.onClick.AddListener(() => RemapAction(movementReference.action, 1));
        leftButton.onClick.AddListener(() => RemapAction(movementReference.action, 2));
        rightButton.onClick.AddListener(() => RemapAction(movementReference.action, 3));
    }


    public void RemapAction(InputAction action, int? targetBinding = null)
    {
        var remapOperation = action.PerformInteractiveRebinding();
        remapOperation.WithCancelingThrough("<Keyboard>/escape");
        remapOperation.WithControlsExcluding("Mouse");
        remapOperation.OnMatchWaitForAnother(0.1f);
        remapOperation.WithBindingGroup("MouseKeyboard");

        if (targetBinding.HasValue)
        {
            remapOperation.WithTargetBinding(targetBinding.Value);
        }
      
        remapOperation.Start();
    }

    private void UpdateText()
    {
        forwardText.text = InputControlPath.ToHumanReadableString(movementReference.action.bindings[0].effectivePath);
        leftText.text = InputControlPath.ToHumanReadableString(movementReference.action.bindings[1].effectivePath);
        backwardText.text = InputControlPath.ToHumanReadableString(movementReference.action.bindings[2].effectivePath);
        rightText.text = InputControlPath.ToHumanReadableString(movementReference.action.bindings[3].effectivePath);
    }

}

What’s the correct way to do this ? I searched through the doc and github to no avail :confused:

Thanks !

EDIT : Also, I’m on AZERTY layout but my Z key is displayed as W (because of the location mapping). Is there a way to get the “real” name of the control ?

I found a way to display the “real” value :

Instead of using :
InputControlPath.ToHumanReadableString(movementReference.action.bindings[2].effectivePath, InputControlPath.HumanReadableStringOptions.OmitDevice)

I’m using :
movementReference.action.controls[0].displayName

Still don’t know how I’m supposed to get the indexes (hardcode it ?) or how to rebind at runtime though :confused:

1 Like

Bump

Sorry for the long lag here.

We’re in the process of putting the finishing touches on an extensive pass over binding display strings and rebinding UI support including a sample for how to put together a rebind UI using the APIs (and also including a ready-made prefab and scripting component that you can use in your own projects). This includes support for dealing with composites both in case you want to rebind them as a whole as well as when splitting them up into individual rebinds. The changes should be in the next package.

Addendum, this should also work out of the box with the incoming changes.

Oh wow, that’s cool ! Looking forward to it as I still couldn’t make it work (and yes, what I wanted to do is split a composite so I could remap each)

Any updates on rebinding composite axis?

I am doing

ActionToRebind.action.PerformInteractiveRebinding()
            .WithControlsExcluding("Mouse")
            .WithCancelingThrough("<Keyboard>/escape")
            .WithTargetBinding(0)
            .WithBindingGroup("Keyboard")
            .OnMatchWaitForAnother(0.1f)
            .Start()

And the “onComplete” never triggers and I can’t remap. Tried without the BindingGroup, without the BindingTarget, without the ExcludeMouse, Without the Cancel, tried all combinations and I keep failing. What am I missing?

Bump, still finding a way to rebind a composite.

1 Like

And I mean individually rebind each key, not the weird rebinding that is present in the (uncommented) code in the tanks demo.

Yeah, I still need it too, and no sign of this new package either :confused:

Finally managed to get something working.

So the trick is that a composite is a binding, but it’s also made of bindings. So for this action :

action.bindings[0] is WASD, and is a 2D vector. PerformInteractiveRebinding will only accept a 2D vector like a left stick.

However action.bindings[1] is the W key ! If you do PerformInteractiveRebinding with WithTargetBinding(1) you can rebind it. The only problem is that it will still only accept a 2D vector, so you need to use useWithExpectedControlType(“Button”).

Here’s my script (use it on a button) you’ll need to set the bindingIndex int manually in editor depending on your setup.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;
using TMPro;
using UnityEngine.EventSystems;

public class RebindButton : MonoBehaviour
{
    public InputActionReference actionReference;
    public int inputBindingIndex;    
    public TextMeshProUGUI buttonText;

    private InputActionRebindingExtensions.RebindingOperation rebindingOperation;
    private Button button;

    private void OnValidate()
    {
        inputBindingIndex = Mathf.Clamp(inputBindingIndex, 0, actionReference.action.bindings.Count-1);
    }

    void Start()
    {
        button = GetComponentInChildren<Button>();

        button.onClick.AddListener(StartRebind);

        buttonText.text = InputControlPath.ToHumanReadableString(actionReference.action.bindings[inputBindingIndex].effectivePath);
    }

    private void OnDestroy()
    {
        button.onClick.RemoveAllListeners();
    }

    public void StartRebind()
    {
        actionReference.action.Disable();

        print("launching rebind");

        var rebindingOperation = actionReference.action.PerformInteractiveRebinding()
                    .WithControlsExcluding("Mouse/delta")
                    .WithControlsExcluding("Mouse/position")
                    .WithCancelingThrough("<Keyboard>/escape")
                    .WithTargetBinding(inputBindingIndex)
                    .Start()
                    .OnComplete((x) =>
                    {
                        // update button string
                        buttonText.text = InputControlPath.ToHumanReadableString(x.action.bindings[inputBindingIndex].effectivePath);

                        actionReference.action.Enable();
                        x.Dispose();
                    })
                    .OnCancel((x)=>
                    {
                        // deselect button
                        FindObjectOfType<EventSystem>().SetSelectedGameObject(null);
                        x.Dispose();
                        actionReference.action.Enable();
                    });

        if (actionReference.action.bindings[inputBindingIndex].isPartOfComposite)
        {
            rebindingOperation.WithExpectedControlType("Button");
        }
    }
}

5373045--543852--upload_2020-1-15_11-50-40.png

4 Likes

Sorry, wanted to have all this out before Christmas but things came together slower than expected and I really needed a break…

The PR with the rebinding changes and a new rebinding UI sample has landed and is on the develop branch. 1.0.0-preview.4 is slated for early next week.

Will it break my workaround ?

No problem, everyone needs a break sometime.

Hope it will fix some problems I have and make things like rebinds easier, then the new input system will be in a pretty good state !

I’ve recorded a video with a quick fly-through of the changes.

If you get a chance to give the new stuff a try, let me know it goes :slight_smile:

Think it should be fine. Most of the changes have been mere extensions and shouldn’t be break. PerformInteractiveRebinding() did change a bit under the hood to perform more configuration out of the box but if that should get in the way, you can just reset things to get back to how it was before.

1 Like

You rock ! Changes you’ve made to the composite rebindings are excellent, thank you.

EDIT : just saw the display options and I love it, this will come really handy for tutorials and contextual actions.

Can’t test it right now, but your video shows that it’s really good !

Will try as soon as I can :slight_smile:

Some feedback : I like that the display strings are localized using system language, but I haven’t found a way to manually set it. I’m a french dev making games in english and seeing “press ESPACE to jump” is a bit jarring.

Then localize the whole text ! If you want I’ve a really simple localization system that you can use :smile:

We already do that, but I prefer to play games in english and so do many of my peers.