見出し画像

NDI™で送信するRTAイベントタイマーをUnityで作る #04 キーボード入力編

前回、コントローラーでストップウォッチが動いたので今度はキーボードで動かしましょう。イベントでは技術担当が操作するイメージです

誤操作防止

イベント中はパソコンを他の用途にも使いたいので、キーボードでの操作はCtrl キーや Alt キーを同時押ししないと動かないようにします。さらに、通常操作できない F13~F24 キーを割り当てて、どうやっても誤操作がおきないようにします

KeyboardInput.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;

public class KeyboardInput : MonoBehaviour
{
    [DllImport("user32.dll")]
    static extern bool GetKeyboardState(byte[] lpKeyState);
    const int VK_F13 = 0x7C;
    const int VK_F14 = 0x7D;
    const int VK_F15 = 0x7E;
    const int VK_F16 = 0x7F;
    const int VK_F17 = 0x80;
    const int VK_F18 = 0x81;
    const int VK_F19 = 0x82;
    const int VK_F20 = 0x83;
    const int VK_F21 = 0x84;
    const int VK_F22 = 0x85;
    const int VK_F23 = 0x86;
    const int VK_F24 = 0x87;
    const int VK_LCONTROL = 0xA2;
    const int VK_RSHIFT = 0xA1;
    const int VK_RMENU = 0xA5;

    private byte[] old_key = null;

    [SerializeField] CountUpMain countup;

    // Start is called before the first frame update
    void Start()
    {
        old_key = new byte[256];
        GetKeyboardState(old_key);
    }

    // Update is called once per frame
    void Update()
    {
        byte[] now_key = new byte[256];
        GetKeyboardState(now_key);

        if (countup != null)
        {
            if (isKeyDown(now_key, VK_LCONTROL) == true && isKeyDown(now_key, VK_RMENU) == true)
            {
                if (isKeyDown(now_key, VK_F13) == true && isKeyDown(old_key, VK_F13) == false)
                {
                    countup.input_status = CountUpMain.InputStatus.start;
                }
                if (isKeyDown(now_key, VK_F14) == true && isKeyDown(old_key, VK_F14) == false)
                {
                    countup.input_status = CountUpMain.InputStatus.stop;
                }
                if (isKeyDown(now_key, VK_F15) == true && isKeyDown(old_key, VK_F15) == false)
                {
                    countup.input_status = CountUpMain.InputStatus.pause;
                }
                if (isKeyDown(now_key, VK_F16) == true && isKeyDown(old_key, VK_F16) == false)
                {
                    countup.input_status = CountUpMain.InputStatus.resume;
                }
                if (isKeyDown(now_key, VK_F17) == true && isKeyDown(old_key, VK_F17) == false)
                {
                    countup.input_status = CountUpMain.InputStatus.reset;
                }
            }
            if (isKeyDown(now_key, VK_LCONTROL) == true && isKeyDown(now_key, VK_RSHIFT) == true)
            {
                if (isKeyDown(now_key, VK_F13) == true && isKeyDown(old_key, VK_F13) == false)
                {
                    countup.input_status = CountUpMain.InputStatus.up_1sec;
                }
                if (isKeyDown(now_key, VK_F14) == true && isKeyDown(old_key, VK_F14) == false)
                {
                    countup.input_status = CountUpMain.InputStatus.down_1sec;
                }
                if (isKeyDown(now_key, VK_F15) == true && isKeyDown(old_key, VK_F15) == false)
                {
                    countup.input_status = CountUpMain.InputStatus.up_10sec;
                }
                if (isKeyDown(now_key, VK_F16) == true && isKeyDown(old_key, VK_F16) == false)
                {
                    countup.input_status = CountUpMain.InputStatus.down_10sec;
                }
                if (isKeyDown(now_key, VK_F17) == true && isKeyDown(old_key, VK_F17) == false)
                {
                    countup.input_status = CountUpMain.InputStatus.up_1min;
                }
                if (isKeyDown(now_key, VK_F18) == true && isKeyDown(old_key, VK_F18) == false)
                {
                    countup.input_status = CountUpMain.InputStatus.down_1min;
                }
                if (isKeyDown(now_key, VK_F19) == true && isKeyDown(old_key, VK_F19) == false)
                {
                    countup.input_status = CountUpMain.InputStatus.up_10min;
                }
                if (isKeyDown(now_key, VK_F20) == true && isKeyDown(old_key, VK_F20) == false)
                {
                    countup.input_status = CountUpMain.InputStatus.down_10min;
                }
                if (isKeyDown(now_key, VK_F21) == true && isKeyDown(old_key, VK_F21) == false)
                {
                    countup.input_status = CountUpMain.InputStatus.up_1hour;
                }
                if (isKeyDown(now_key, VK_F22) == true && isKeyDown(old_key, VK_F22) == false)
                {
                    countup.input_status = CountUpMain.InputStatus.down_1hour;
                }
                if (isKeyDown(now_key, VK_F23) == true && isKeyDown(old_key, VK_F23) == false)
                {
                    // 割り当てなし
                }
                if (isKeyDown(now_key, VK_F24) == true && isKeyDown(old_key, VK_F24) == false)
                {
                    countup.input_status = CountUpMain.InputStatus.updown_reset;
                }
            }
        }
        Array.Copy(now_key, old_key, 256);
    }

    bool isKeyDown(byte[] key, int code)
    {
        return ((key[code] & 0x80) != 0);
    }
}

バックグラウンドで動作可能

タイマーを操作する瞬間に別の操作をしている可能性もあるので、ウィンドウにフォーカスがなくても動くようにします。他の方法だとうまく動かなかったので、一番シンプルに Win32 API からキーボードの状態を取得します

そのままだとキーボードを押したままなのか、押した瞬間なのかわからないので、1フレーム前の状態を保持しておき変化した場合のみ CountUpMain のinput_status プロパティを変更します

Stream Deck 使用

修飾キーの左右を判別できるため、ストップウォッチの場合は左 Ctrl キーに右の Alt か Shift を組み合わせて押し、その状態で F13~F24 を押した際にいろいろな動作をするようにします。普通のキーボードでは押せないので、ここでは Elgato の Stream Deck を使います。ホットキー入力を設定することにより、1ボタンで各操作ができるようになります。間違えて押してしまわないように、階層を深く設定しています

トップがこれ
右上のストップウォッチを押してから、各種操作を行う
細かい時間の修正も可能

正しく動作することを確認します

おつかれさまでした

キーボード入力を作成しましたが、本番では走者&解説の方にスタート&ストップを任せるため、技術担当はリセット以外操作する必要がないことになります。マウスで操作ができたら要らないかもですね。
次はカウントダウンタイマーを作りましょう

いいなと思ったら応援しよう!