防止热键重叠 [Unity]

Prevent Hotkeys Overlapping [Unity]

我写了一个 'Hotkey' class 可以很容易地使用一些修饰键(shift、ctrl、cmd...)评估单个给定的键。

此 class 能够判断,与任何其他 KeyControl 一样,如果正在按下热键,或者它是否在当前帧上 pressed/released。当这些事情发生时,也能够发出 C# 事件。

代码就像一个魅力,除了一件事,例如,如果我想检查热键 Ctrl+Z 我创建实例是这样的:
var ctrlZ = new Hotkey(Key.Z, ModifierKeys.LeftShift);

Key 是一个 Unity 枚举,ModifierKeys 是一个标记的自定义枚举,它定义了最常用的修改键。

然后,要检查它,我只需要调用:
if (ctrlZ.isPressed) { // code here... }

当我按下组合键 Ctrl+Shift+Z 时,问题就来了,上面的代码在我不想要的时候计算为真,因为我需要用 [=11 做一件事=] 热键和另一个带有 Ctrl+Shift+Z 的热键(重叠)。

我正在使用新的输入系统,并且为了评估热键,我只是遍历修饰符 KeyControl 对象的列表并检查它们是否与给定的 [=13] 同时被按下=].

那么问题是:如何防止这种重叠?

我尝试了最基本的解决方案,即将所有未使用的修饰符 KeyControl 对象添加到另一个列表并检查其中的 none 是否被按下,但我想知道是否有比以这种方式检查它更简单、更有效的解决方案(它是在更新循环内完成的,因此,如果以这种方式评估许多热键,它可能会导致一些开销,我认为)。

同时 the docs suggest that this isn't exactly implemented by Unity because it talks about synthetic keys combining right and left shift into shiftKey but not about anything related to this, I think there are multiple ways to better implement this for yourself perhaps by directly hooking into one of the input events or extension methods in the Keyboard control.

好吧,虽然我在对 Daniel Bereza 的评论中说过我决定保留它,但我有最后一个想法,这似乎确实是我正在寻找的解决方案,而且它奏效了!

我决定自己回答,以防其他人需要解决此问题。

基本上,我所做的是在每一帧检查是否按下了热键的修改键 是否按下了其他键的 none。这是通过 2 个循环完成的(一个用于需要按下的修饰符,另一个用于不需要按下的修饰符)。

为了替换那些循环,我使用了按位运算。诀窍是存储一个带标记的枚举(在我的类型 ModifierKeys 的情况下),它告诉当前按下的修改键和另一个带标记的枚举,它是互补的,比方说:

private static ModifierKeys pressedModifiers
private static ModifierKeys unpressedModifiers

这两个字段通过 InputAction 之前绑定到每个修饰键的事件更新,因此当按下修饰键时,您将 ModifierKey 标志添加到 pressedModifiers并同时更新补充 unpressedModifiers.

然后检查是否按下,例如,很简单:

public bool IsPressed => modifiersToBePressed == pressedModifiers && ((int) (unpressedModifiers & modifiersToBePressed) == 0) && Keyboard.current[this.Key].isPressed

就是这样,如果您想检查它是否在当前帧上被按下或释放,只需根据需要更改 Keyboard.current[this.Key].isPressed 部分即可。

我希望我已经解释清楚了。我测试了它,它似乎按预期工作,除非有一些我没有考虑到的东西。我还测试了性能,而对于 600 次热键评估,另一种方法平均需要 100-200 微秒,而这种变通方法只需要 5-15。这对我来说是一个很大的进步。