【Unity_Input System】Input System新输入系统(三)——在游戏中更换按键绑定

八、在游戏中更换按键绑定

1.Binding和CompositeBinding

  • Binding只由一个部分组成,一般绑定单个按键或者摇杆
  • CompositeBinding由两个以上部分组成,一般是用于将多个按键组成虚拟轴

更换按键绑定时,Binding和Composite Binding需要分别处理,对Composite Binding需要循环各个部分进行修改。

可以用InputBinding.isComposite来判断是否是Composite Binding

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CompositeBindingCheck : MonoBehaviour
{
    MyAction inputActions;
    private void Start()
    {
        inputActions = new MyAction();
        Debug.Log("WASD:" + inputActions.Player.Test1.bindings[0].isComposite);
        Debug.Log("Left Stick:" + inputActions.Player.Test1.bindings[1].isComposite);
    }

}

 控制台输出结果:

2.在UI中显示原本的按键绑定

1>显示Action名称

用到的API:InputAction.name

2>显示Binding名称

用到的API:InputAction.GetBindingDisplayString

using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;

public class RebindButtonTest : MonoBehaviour
{
    //Action名
    public Text actionLabel;
    //Binding名
    public Text bindingLabel;
    [SerializeField]
    public InputActionReference actionReference;
    private void Start()
    {
        actionLabel.text = actionReference.name;
        bindingLabel.text = actionReference.action.GetBindingDisplayString(0, out string deviceLayoutName, out string controlPath);
    }

}

运行效果:

PS.InputActionReferrence是InputSystem的一个API,用来引用在InputActionAsset中已经设置过的InputAction,即使被引用的InputAction重命名,引用也不会丢失,

请查阅:Class InputActionReference | Input System | 1.3.0 (unity3d.com)

3.更换按键绑定

用到的API:InputAction.PerformInteractiveRebinding

请查阅:Class InputActionRebindingExtensions | Input System | 1.3.0 (unity3d.com)

How do I…? | Input System | 1.3.0 (unity3d.com)

注意Binding和Composite Binding需要分别处理,因为Composite Binding需要循环更换每个部分的按键绑定

下面的例子只是简单去测试怎么使用这个API去修改按键绑定,如果是实际项目需要,可以参考官方案例:Rebinding UI

using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;

public class RebindButtonTest : MonoBehaviour
{
    //Action名
    public Text actionLabel;
    //Binding名
    public Text bindingLabel;
    //Binding的id
    public string bindingId;
    
    private int index;
    [SerializeField]
    public InputActionReference actionReference;
    private InputActionRebindingExtensions.RebindingOperation RebindOperation;
    private void Start()
    {
        //UI显示Action名
        actionLabel.text = actionReference.name;
        //获取BingdingID
        /* 这里只是为了简单,所以手动选择要修改的binding,并且获取它的id
         * 这个例子里面,第0层是WASD
         * 第1层是composite的第1个部分W
         * 第2层是composite的第2个部分S
         * 第3层是composite的第3个部分A
         * 第4层是composite的第4个部分D
         * 第5层是左摇杆
         */
        index = 0;
        bindingId = actionReference.action.bindings[index].id.ToString();
        //UI刷新Binding名
        UpdateLabel(index);
    }
    /// <summary>
    /// 开始替换
    /// </summary>
    /// <param name="index"></param>
    public void StartInteractiveRebind()
    {
        //获取bindingIndex,Bingdings数组的下标,从0开始,如果action为空返回false
        if (!CheckActionAndBinding(out int bindingIndex))
            return;

        // If the binding is a composite, we need to rebind each part in turn.
        if (actionReference.action.bindings[bindingIndex].isComposite)
        {
            var firstPartIndex = bindingIndex + 1;
            if (firstPartIndex < actionReference.action.bindings.Count && actionReference.action.bindings[firstPartIndex].isPartOfComposite)
                PerformInteractiveRebind(actionReference, firstPartIndex, allCompositeParts: true);
        }
        else
        {
            PerformInteractiveRebind(actionReference, bindingIndex);
        }
    }
    /// <summary>
    /// 获取bindingIndex,Bingdings数组的下标,从0开始,如果action为空返回false
    /// </summary>
    /// <param name="index"></param>
    private bool CheckActionAndBinding(out int bindingIndex)
    {
        bindingIndex = -1;
        if (actionReference == null)
            return false;
        bindingIndex = actionReference.action.bindings.IndexOf(x => x.id == new System.Guid(bindingId));
        return true;

    }
    private void PerformInteractiveRebind(InputAction action, int bindingIndex, bool allCompositeParts = false)
    {
        RebindOperation?.Cancel(); // Will null out m_RebindOperation.

        void CleanUp()
        {
            RebindOperation?.Dispose();
            RebindOperation = null;
        }

        // Configure the rebind.
        RebindOperation = action.PerformInteractiveRebinding(bindingIndex)
            .WithControlsExcluding("Mouse")//剔除鼠标
            .OnCancel(
                operation =>
                {
                    CleanUp();
                })
            .OnComplete(
                operation =>
                {
                    UpdateLabel(index);
                    CleanUp();

                        // If there's more composite parts we should bind, initiate a rebind
                        // for the next part.
                    if (allCompositeParts)
                    {
                        var nextBindingIndex = bindingIndex + 1;
                        if (nextBindingIndex < action.bindings.Count && action.bindings[nextBindingIndex].isPartOfComposite)
                            PerformInteractiveRebind(action, nextBindingIndex, true);
                    }
                });
        RebindOperation.Start();
    }
    /// <summary>
    /// UI刷新binding名
    /// </summary>
    /// <param name="index"></param>
    private void UpdateLabel(int index)
    {
        bindingLabel.text = actionReference.action.GetBindingDisplayString(index, out string deviceLayoutName, out string controlPath);
    }
}

运行效果:

 

4.按键的保存

//Load:
var rebinds = PlayerPrefs.GetString("rebinds");
if (!string.IsNullOrEmpty(rebinds))
actions.LoadBindingOverridesFromJson(rebinds);

//Save:
var rebinds = actions.SaveBindingOverridesAsJson();
PlayerPrefs.SetString("rebinds", rebinds);

5.按键恢复默认设置

用到的API:InputAction.RemoveBindingOverride

请查阅:Class InputActionRebindingExtensions | Input System | 1.3.0 (unity3d.com)

using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;

public class RebindButtonTest : MonoBehaviour
{
    //Action名
    public Text actionLabel;
    //Binding名
    public Text bindingLabel;
    //Binding的id
    public string bindingId;
    
    private int index;
    [SerializeField]
    public InputActionReference actionReference;
    private InputActionRebindingExtensions.RebindingOperation RebindOperation;
    private void Start()
    {
        //UI显示Action名
        actionLabel.text = actionReference.name;
        //获取BingdingID
        /* 这里只是为了简单,所以手动选择要修改的binding,并且获取它的id
         * 这个例子里面,第0层是WASD
         * 第1层是composite的第1个部分W
         * 第2层是composite的第2个部分S
         * 第3层是composite的第3个部分A
         * 第4层是composite的第4个部分D
         * 第5层是左摇杆
         */
        index = 0;
        bindingId = actionReference.action.bindings[index].id.ToString();
        //UI刷新Binding名
        UpdateLabel(index);
    }
    //开始替换
    public void StartInteractiveRebind()
    {
        //获取bindingIndex,Bingdings数组的下标,从0开始,如果action为空返回false
        if (!CheckActionAndBinding(out int bindingIndex))
            return;

        // If the binding is a composite, we need to rebind each part in turn.
        if (actionReference.action.bindings[bindingIndex].isComposite)
        {
            var firstPartIndex = bindingIndex + 1;
            if (firstPartIndex < actionReference.action.bindings.Count && actionReference.action.bindings[firstPartIndex].isPartOfComposite)
                PerformInteractiveRebind(actionReference, firstPartIndex, allCompositeParts: true);
        }
        else
        {
            PerformInteractiveRebind(actionReference, bindingIndex);
        }
    }
    //获取bindingIndex,Bingdings数组的下标,从0开始,如果action为空返回false
    private bool CheckActionAndBinding(out int bindingIndex)
    {
        bindingIndex = -1;
        if (actionReference == null)
            return false;
        bindingIndex = actionReference.action.bindings.IndexOf(x => x.id == new System.Guid(bindingId));
        return true;

    }
    private void PerformInteractiveRebind(InputAction action, int bindingIndex, bool allCompositeParts = false)
    {
        RebindOperation?.Cancel(); // Will null out m_RebindOperation.

        void CleanUp()
        {
            RebindOperation?.Dispose();
            RebindOperation = null;
        }

        // Configure the rebind.
        RebindOperation = action.PerformInteractiveRebinding(bindingIndex)
            .WithControlsExcluding("Mouse")//剔除鼠标
            .OnCancel(
                operation =>
                {
                    CleanUp();
                })
            .OnComplete(
                operation =>
                {
                    UpdateLabel(index);
                    CleanUp();

                        // If there's more composite parts we should bind, initiate a rebind
                        // for the next part.
                    if (allCompositeParts)
                    {
                        var nextBindingIndex = bindingIndex + 1;
                        if (nextBindingIndex < action.bindings.Count && action.bindings[nextBindingIndex].isPartOfComposite)
                            PerformInteractiveRebind(action, nextBindingIndex, true);
                    }
                });
        RebindOperation.Start();
    }
    /// <summary>
    /// UI刷新binding名
    /// </summary>
    /// <param name="index"></param>
    private void UpdateLabel(int index)
    {
        bindingLabel.text = actionReference.action.GetBindingDisplayString(index, out string deviceLayoutName, out string controlPath);
    }

    public void ResetToDefault()
    {
        if (!CheckActionAndBinding(out var bindingIndex))
            return;

        if (actionReference.action.bindings[bindingIndex].isComposite)
        {
            // It's a composite. Remove overrides from part bindings.
            for (var i = bindingIndex + 1; i < actionReference.action.bindings.Count && actionReference.action.bindings[i].isPartOfComposite; ++i)
                actionReference.action.RemoveBindingOverride(i);
        }
        else
        {
            actionReference.action.RemoveBindingOverride(bindingIndex);
        }
        UpdateLabel(index);
    }
}

运行效果: