【Unity】【UI Toolkit】キーボードの押下イベントをハンドリングする

UnityのUI Toolkitでキーボードの押下イベントをハンドリングする方法についてまとめました。

Unity 2022.3.13f1

KeyDownEventとKeyUpEvent

キーボードの押下・離上をハンドリングするには、以下のようにKeyDownEventKeyUpEventを使います。

using System;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

public sealed class Example : EditorWindow
{
    private void CreateGUI()
    {
        var container = new VisualElement();
        container.name = "container";
        container.Add(new Label("Press any key to see the event"));
        container.Add(new TextField());
        container.style.flexGrow = 1.0f;

        // キー押下イベントのリスナを登録
        container.RegisterCallback<KeyDownEvent>(evt =>
            {
                var message = $"KeyDown: {evt.target}{Environment.NewLine}" +
                              $"    keyCode: {evt.keyCode}{Environment.NewLine}" + // 押下されたキーボードのキー
                              $"    modifiers: {evt.modifiers}{Environment.NewLine}" + // Shift, Controlキーなどが押下されていたらここに入ってくる
                              $"    character: {evt.character}";
                Debug.Log(message);
            },
            TrickleDown.TrickleDown); // TextFieldはイベントの伝播を止めるため、この例ではTrickleDownフェイズ時点でハンドリングしている

        // キー離上イベントのリスナを登録
        container.RegisterCallback<KeyUpEvent>(evt =>
            {
                var message = $"KeyUp: {evt.target}{Environment.NewLine}" +
                              $"    keyCode: {evt.keyCode}{Environment.NewLine}" +
                              $"    modifiers: {evt.modifiers}{Environment.NewLine}" +
                              $"    character: {evt.character}";
                Debug.Log(message);
            },
            TrickleDown.TrickleDown);

        rootVisualElement.Add(container);
    }

    [MenuItem("Example/Example")]
    public static void ShowWindow()
    {
        GetWindow<Example>();
    }
}

説明はコメントに書いた通りです。
Example > Exampleメニューからウィンドウを開いてテキストフィールドに文字を入力するとログが出力されることを確認できます。

なおUI Toolkitにおけるイベント伝播の仕組みについて(RegisterCallbackTrickeDownなどについて)は以下の記事にまとめていますので、必要に応じて参照してください。

light11.hatenadiary.com

characterプロパティについて

KeyDownEventでキーボードのどの物理キーが押下されたかを取得するには、前節の通りKeyDownEvent.keyCodeKeyDownEvent.modifiersを参照します。

これに対して、文字キーが押下されたときにはKeyDownEvent.characterに値が入ったイベントが追加で発行されます。
characterkeyCodeとは異なり、物理キーではなく入力された文字を表します。
例えば大文字や小文字を判別したい場合にはこちらのイベントおよびcharacterプロパティを使用します。

フォーカスについて

KeyDownEventKeyUpEventはフォーカスされているVisual Elementをターゲットとして発行されます。
すなわち、Visual Elementがfocusableであり、かつフォーカスされていないといけません。

ここまでの例では、focusableなVisuale ElementはTextFieldのみなので、TextField上でしかログが出力されないことを確認できるはずです。

もしテキストフィールド以外でイベントをハンドリングしたい場合には、以下のようにVisual Elementをfocusableにします(containerをfocusableにしています)。

using System;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

public sealed class Example : EditorWindow
{
    private void CreateGUI()
    {
        var container = new VisualElement();
        container.name = "container";
        container.Add(new Label("Press any key to see the event"));
        container.Add(new TextField());
        container.style.flexGrow = 1.0f;
        
        // containerもフォーカスをできるように
        container.focusable = true;

        container.RegisterCallback<KeyDownEvent>(evt =>
            {
                var message = $"KeyDown: {evt.target}{Environment.NewLine}" +
                              $"    keyCode: {evt.keyCode}{Environment.NewLine}" +
                              $"    modifiers: {evt.modifiers}{Environment.NewLine}" +
                              $"    character: {evt.character}";
                Debug.Log(message);
            },
            TrickleDown.TrickleDown);

        container.RegisterCallback<KeyUpEvent>(evt =>
            {
                var message = $"KeyUp: {evt.target}{Environment.NewLine}" +
                              $"    keyCode: {evt.keyCode}{Environment.NewLine}" +
                              $"    modifiers: {evt.modifiers}{Environment.NewLine}" +
                              $"    character: {evt.character}";
                Debug.Log(message);
            },
            TrickleDown.TrickleDown);

        rootVisualElement.Add(container);
    }

    [MenuItem("Example/Example")]
    public static void ShowWindow()
    {
        GetWindow<Example>();
    }
}

Example > Exampleメニューからウィンドウを開くと、今度はどこでキーボードを入力してもイベントがハンドリングできていることを確認できます。

参考

docs.unity3d.com