FishPlayer

一个喜欢摸鱼的废物

0%

Unity InputSystem 简单保存按键rebind

Unity 的 InputSystem 正式发布也好一段时间了。本来这边也想自己做一套输入处理但是看 Unity 这个官方的还挺方便就打算直接用。

InputAction

说下我对这个 InputAction 的简单理解。

InputAction

- Composite
    - Binding
- Binding
  • Interaction
  • Processor

InputAction 是定义的最上层的交互事件,我认为他是直接和 gameplay 层面交互的,提供响应事件和输入设备的数据(比如鼠标的位置,或是线性按钮的数值)

Binding 是具体到输入设备上按键,线性输入的一个数据结构

Composite 是把 Binding 提供的直接输入数据处理成我们想要的数据并提供给 InputAction。

另外两个可以应用于InputAction,Composite 和 Binding 的组件

Interaction 是用来规定当前的 输入/行为 触发的机制。

Processor 是用来对当前的输入数据做追加处理的。

rebind

我在官方的tank example中做的 rebind save

对于简单的 Button Action 里的 Binding, rebind 的时候我们是追加了 OverridePath 给这个 Button Action 下面的 Binding。

Path 是一个字符串, 大概是这样的 “/buttonSouth”。 里面包含了输入设备类型和具体的按钮名。(其实我觉得, 这样的文本可以提供成一个Const string ,会好用很多)

对于简单的 rebind 存储,我们只需要找到所有的 Binding 里的 override path 保存下来,下次启动游戏加载了原本的 Action Map 之后再把 Override Path 给赋值上去。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

public static class InputBindSaveHelper
{
static readonly string s_keyMappingPreferKey = "KeyMapping";

public static void SaveActionMapRebinding(InputActionMap actionMap)
{
Dictionary<Guid, string> overridedActionPathDict = new Dictionary<Guid, string>();
UnityEngine.InputSystem.Utilities.ReadOnlyArray<InputAction> actions = actionMap.actions;
for (int i = 0; i < actions.Count; i++)
{
InputAction action = actions[i];
UnityEngine.InputSystem.Utilities.ReadOnlyArray<InputBinding> actionBindings = action.bindings;
for (int j = 0; j < actionBindings.Count; j++)
{
InputBinding actionBinding = actionBindings[j];
if (string.IsNullOrEmpty(actionBinding.overridePath))
continue;
overridedActionPathDict.Add(actionBinding.id, actionBinding.overridePath);
}
}

BinaryFormatter binFormatter = new BinaryFormatter();
MemoryStream mStream = new MemoryStream();
binFormatter.Serialize(mStream, overridedActionPathDict);

byte[] byteArr = mStream.ToArray();
// idk why [Encoding.UTF8.GetString()] just doesnt work
string encodingString = Convert.ToBase64String(byteArr, Base64FormattingOptions.InsertLineBreaks);
PlayerPrefs.SetString(s_keyMappingPreferKey, encodingString);
}

public static void LoadActionMapRebinding(InputActionMap actionMap)
{
string encodingString = PlayerPrefs.GetString(s_keyMappingPreferKey);
byte[] byteArr = Convert.FromBase64String(encodingString);

BinaryFormatter binFormatter = new BinaryFormatter();
MemoryStream mStream = new MemoryStream(byteArr);
Dictionary<Guid, string> rebindSavePairDict = binFormatter.Deserialize(mStream) as Dictionary<Guid, string>;

if (rebindSavePairDict != null)
{
UnityEngine.InputSystem.Utilities.ReadOnlyArray<InputAction> actions = actionMap.actions;
for (int i = 0; i < actions.Count; i++)
{
InputAction action = actions[i];
UnityEngine.InputSystem.Utilities.ReadOnlyArray<InputBinding> actionBindings = action.bindings;
for (int j = 0; j < actionBindings.Count; j++)
{
InputBinding actionBinding = actionBindings[j];
// override
if (rebindSavePairDict.ContainsKey(actionBinding.id))
{
actionBinding.overridePath = rebindSavePairDict[actionBinding.id];
action.ApplyBindingOverride(actionBinding);
}
}
}

}
}
}

当前因为项目里的存档相关的功能还没做完,结构还没定下来,所以我只能把 rebind 存在PlayerPrefs里。

总结

我也只是大概看了一下 Unity 的这个 InputSystem,没有很深的去使用。但是初步上手的时候就感觉他的封装和扩展性对于那些操作要求不高的游戏是已经够用的了。
比较麻烦的一个点就是他的 Interaction, Processor 和 Binding 的覆盖全都需要用 string。直接写的 string 会非常不方便,估计使用者得自己总结一些其常用的 const string 写进变量直接调用。
这样才能比较好的维持代码可读性。