使用EventSystem处理按键事件

12

场景:比如说你想检查键盘上是否按下了"W"键,最常见的方法是通过以下代码进行检查:

void Update(){
    if (Input.GetKeyDown("W"))
        DoSomething();
}

有没有一种使用Unity的EventSystem来实现以下功能的方法?换句话说,是否有像IPointerClickHandler这样的接口实现,可以检查按钮是否被按下,而不需要在Update()函数中这样做?

新输入系统使用事件。它仍在开发中,但您可以下载实验性预览版。 - Hellium
@Hellium,有一个新的输入系统,但它不支持这个功能,也不是为了替换EventSystem而开发的。 - PassetCronUs
我从未说过新的输入系统旨在取代“EventSystem”。我指出,这个新系统使用事件,不需要程序员在Update函数中每一帧检查按键是否被按下。 据我所知,没有办法使用EventSystem来检查按键是否被按下。 - Hellium
@Hellium 好的,我明白了。 - PassetCronUs
2个回答

18
使用Unity的EventSystem,有没有一种方法可以实现类似IPointerClickHandler接口的功能?换句话说,是否有实现这样一个接口的方法?
答:不行。EventSystem主要用于光线投射和事件分发。不用于检测键盘事件。EventSystem中唯一能检测键盘事件的组件是InputField组件,只能用于InputField,不能用于其他组件。
如何在不使用Update()函数的情况下检查按钮是否被按下?
答:可以使用Event.KeyboardEvent,在OnGUI函数中实现。
void OnGUI()
{
    if (Event.current.Equals(Event.KeyboardEvent("W")))
    {
        print("W pressed!");
    }
}

使用Update函数和Input.GetKeyDown函数相比,这种方法更糟糕。我建议你坚持使用Input.GetKeyDown,这样没有任何问题。


如果您正在寻找不使用Input.GetKeyDown的事件类型InputSystem,则可以使用Unity的新输入API并订阅InputSystem.onEvent事件。
如果您正在寻找类似于IPointerClickHandler接口的功能,则可以在Input.GetKeyDown上实现它。
1.首先,使用System.Enum.GetValues(typeof(KeyCode));获取所有KeyCode枚举并将其存储在数组中。
2.创建一个名为“ IKeyboardEvent”的接口,并添加函数,例如OnKeyDown,就像在IPointerClickHandler接口中的OnPointerClick一样。
3.从#1循环遍历KeyCode,并检查数组中的每个键是按下、释放还是按下。
4.获取场景中的所有组件,并检查它们是否实现了IKeyboardEvent接口。如果是,则根据#3中的键状态调用接口中的适当函数。
这是一个功能示例,仍然可以扩展或改进:
附加到空GameObject。
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class KeyboardEventSystem : MonoBehaviour
{
    Array allKeyCodes;

    private static List<Transform> allTransforms = new List<Transform>();
    private static List<GameObject> rootGameObjects = new List<GameObject>();

    void Awake()
    {
        allKeyCodes = System.Enum.GetValues(typeof(KeyCode));
    }

    void Update()
    {
        //Loop over all the keycodes
        foreach (KeyCode tempKey in allKeyCodes)
        {
            //Send event to key down
            if (Input.GetKeyDown(tempKey))
                senEvent(tempKey, KeybrdEventType.keyDown);

            //Send event to key up
            if (Input.GetKeyUp(tempKey))
                senEvent(tempKey, KeybrdEventType.KeyUp);

            //Send event to while key is held down
            if (Input.GetKey(tempKey))
                senEvent(tempKey, KeybrdEventType.down);

        }
    }


    void senEvent(KeyCode keycode, KeybrdEventType evType)
    {
        GetAllRootObject();
        GetAllComponents();

        //Loop over all the interfaces and callthe appropriate function
        for (int i = 0; i < allTransforms.Count; i++)
        {
            GameObject obj = allTransforms[i].gameObject;

            //Invoke the appropriate interface function if not null
            IKeyboardEvent itfc = obj.GetComponent<IKeyboardEvent>();
            if (itfc != null)
            {
                if (evType == KeybrdEventType.keyDown)
                    itfc.OnKeyDown(keycode);
                if (evType == KeybrdEventType.KeyUp)
                    itfc.OnKeyUP(keycode);
                if (evType == KeybrdEventType.down)
                    itfc.OnKey(keycode);
            }
        }
    }

    private static void GetAllRootObject()
    {
        rootGameObjects.Clear();

        Scene activeScene = SceneManager.GetActiveScene();
        activeScene.GetRootGameObjects(rootGameObjects);
    }


    private static void GetAllComponents()
    {
        allTransforms.Clear();

        for (int i = 0; i < rootGameObjects.Count; ++i)
        {
            GameObject obj = rootGameObjects[i];

            //Get all child Transforms attached to this GameObject
            obj.GetComponentsInChildren<Transform>(true, allTransforms);
        }
    }

}

public enum KeybrdEventType
{
    keyDown,
    KeyUp,
    down
}

public interface IKeyboardEvent
{
    void OnKeyDown(KeyCode keycode);
    void OnKeyUP(KeyCode keycode);
    void OnKey(KeyCode keycode);
}

用法:

像使用IPointerClickHandler一样,在脚本中实现IKeyboardEvent接口及其函数。

public class test : MonoBehaviour, IKeyboardEvent
{
    public void OnKey(KeyCode keycode)
    {
        Debug.Log("Key held down: " + keycode);
    }

    public void OnKeyDown(KeyCode keycode)
    {
        Debug.Log("Key pressed: " + keycode);
    }

    public void OnKeyUP(KeyCode keycode)
    {
        Debug.Log("Key released: " + keycode);
    }
}

3
我建议你坚持使用 Input.GetKeyDown 函数,这个函数本身没有问题。不过,如果有一个基于事件驱动的函数,尤其是在键盘驱动程序级别更接近的情况下,会更优秀。如果这个函数是中断驱动而非轮询驱动,那就更好了。遗憾的是,你说 Unity 中并不存在这样的函数。 - John Stock
不幸的是,在Unity 2019.2中遍历所有KeyCode值会触发大量“ArgumentException:Input Button XXX未设置。要更改输入设置,请使用:编辑->设置->输入”。 - SePröbläm

2

我创建了一个简单的脚本来触发简单事件。 我使用了OnguiGUI而不是Update。 请看下面的代码!

using UnityEngine;
using UnityEngine.Events;

public class TriggerKey : MonoBehaviour
{

    [Header("----Add key to trigger on pressed----")]
    public string key;

    // Unity event inspector
    public UnityEvent OnTriggerKey;

    public void OnGUI()
    {    // triiger event on trigger key
        if (Event.current.Equals(Event.KeyboardEvent(key)))
        {
            OnTriggerKey.Invoke();
            print("test trigger btn");
        }

    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接