見出し画像

NDI™で送信するRTAイベントタイマーをUnityで作る #05 カウントダウン編

JAWS PLAYERES では RTA だけでなく企画も行われるので、残り時間が表示できるカウントダウンタイマーも作成しておきます

オブジェクト、背景の設定

ヒエラルキーに CountDown Object を作成。CountDown Background Image も作成。ストップウォッチのときと同じく画像を配置していく

ヒエラルキー

KeyboardInput.cs にカウントダウン用の処理を追加。カウントダウンは右 Ctrl と 左 Shift、左 Alt を組み合わせる

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;

    static int VK_RCONTROL = 0xA3;
    static int VK_LSHIFT = 0xA0;
    static int VK_LMENU = 0xA4;


    private byte[] old_key = null;

    [SerializeField] CountUpMain countup;
    [SerializeField] CountDownMain countdown;

    // 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;
                }
            }
        }

        if (countdown != null)
        {
            if (isKeyDown(now_key, VK_RCONTROL) == true && isKeyDown(now_key, VK_LMENU) == true)
            {
                if (isKeyDown(now_key, VK_F13) == true && isKeyDown(old_key, VK_F13) == false)
                {
                    countdown.input_status = CountDownMain.InputStatus.start;
                }
                if (isKeyDown(now_key, VK_F14) == true && isKeyDown(old_key, VK_F14) == false)
                {
                    countdown.input_status = CountDownMain.InputStatus.stop;
                }
                if (isKeyDown(now_key, VK_F15) == true && isKeyDown(old_key, VK_F15) == false)
                {
                    countdown.input_status = CountDownMain.InputStatus.pause;
                }
                if (isKeyDown(now_key, VK_F16) == true && isKeyDown(old_key, VK_F16) == false)
                {
                    countdown.input_status = CountDownMain.InputStatus.resume;
                }
                if (isKeyDown(now_key, VK_F17) == true && isKeyDown(old_key, VK_F17) == false)
                {
                    countdown.input_status = CountDownMain.InputStatus.reset;
                }
            }
            if (isKeyDown(now_key, VK_RCONTROL) == true && isKeyDown(now_key, VK_LSHIFT) == true)
            {
                if (isKeyDown(now_key, VK_F13) == true && isKeyDown(old_key, VK_F13) == false)
                {
                    countdown.input_status = CountDownMain.InputStatus.up_1sec;
                }
                if (isKeyDown(now_key, VK_F14) == true && isKeyDown(old_key, VK_F14) == false)
                {
                    countdown.input_status = CountDownMain.InputStatus.down_1sec;
                }
                if (isKeyDown(now_key, VK_F15) == true && isKeyDown(old_key, VK_F15) == false)
                {
                    countdown.input_status = CountDownMain.InputStatus.up_10sec;
                }
                if (isKeyDown(now_key, VK_F16) == true && isKeyDown(old_key, VK_F16) == false)
                {
                    countdown.input_status = CountDownMain.InputStatus.down_10sec;
                }
                if (isKeyDown(now_key, VK_F17) == true && isKeyDown(old_key, VK_F17) == false)
                {
                    countdown.input_status = CountDownMain.InputStatus.up_1min;
                }
                if (isKeyDown(now_key, VK_F18) == true && isKeyDown(old_key, VK_F18) == false)
                {
                    countdown.input_status = CountDownMain.InputStatus.down_1min;
                }
                if (isKeyDown(now_key, VK_F19) == true && isKeyDown(old_key, VK_F19) == false)
                {
                    countdown.input_status = CountDownMain.InputStatus.up_10min;
                }
                if (isKeyDown(now_key, VK_F20) == true && isKeyDown(old_key, VK_F20) == false)
                {
                    countdown.input_status = CountDownMain.InputStatus.down_10min;
                }
                if (isKeyDown(now_key, VK_F21) == true && isKeyDown(old_key, VK_F21) == false)
                {
                    countdown.input_status = CountDownMain.InputStatus.up_1hour;
                }
                if (isKeyDown(now_key, VK_F22) == true && isKeyDown(old_key, VK_F22) == false)
                {
                    countdown.input_status = CountDownMain.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)
                {
                    countdown.input_status = CountDownMain.InputStatus.updown_reset;
                }
            }
        }

        Array.Copy(now_key, old_key, 256);
    }

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

CountDownMain.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class CountDownMain : MonoBehaviour
{
    [SerializeField] Image hour1image;
    [SerializeField] Image hour2image;
    [SerializeField] Image colon1image;
    [SerializeField] Image minute1image;
    [SerializeField] Image minute2image;
    [SerializeField] Image colon2image;
    [SerializeField] Image second1image;
    [SerializeField] Image second2image;

    [SerializeField] Sprite[] number = new Sprite[10];

    private float start_time = 0.0f;
    private float now_time = 0.0f;
    private float offset_time = 0.0f;

    public enum TimerStatus
    {
        ready,
        running,
        paused,
        finished,
    }

    public TimerStatus timer_status { get; private set; }

    public enum InputStatus
    {
        ready,
        start,
        stop,
        pause,
        resume,
        reset,
        up_1sec,
        up_10sec,
        up_1min,
        up_10min,
        up_1hour,
        down_1sec,
        down_10sec,
        down_1min,
        down_10min,
        down_1hour,
        updown_reset,
    }

    public InputStatus input_status;

    // Start is called before the first frame update
    void Start()
    {
        InitDisplay();

        timer_status = TimerStatus.ready;
    }
    // Update is called once per frame
    void Update()
    {
        // タイマーの状態で場合分け
        switch (timer_status)
        {
            case TimerStatus.ready:
                // キー入力を待つ
                if (input_status == InputStatus.start)
                {
                    start_time = Time.time;
                    timer_status = TimerStatus.running;
                }
                // 停止中にF17が押されたらリセット
                if (input_status == InputStatus.reset)
                {
                    timer_status = TimerStatus.ready;
                    InitDisplay();
                    break;
                }
                break;
            case TimerStatus.running:
                // 数字を白にしておく
                ChangeColor("#FFFFFFFF");

                now_time = start_time - Time.time; // カウントダウンなのでマイナスになるのが正解
                SetTime(now_time + offset_time);

                // F14が押されたら終了
                if (input_status == InputStatus.stop)
                {
                    timer_status = TimerStatus.finished;
                    break;
                }
                // 一時停止機能をつける
                if (input_status == InputStatus.pause)
                {
                    timer_status = TimerStatus.paused;
                    break;
                }
                break;
            case TimerStatus.paused:
                // 数字をグレーにしておく
                ChangeColor("#808080FF");

                // F14が押されたら終了
                if (input_status == InputStatus.stop)
                {
                    timer_status = TimerStatus.finished;
                    break;
                }
                // 停止中にF17が押されたらリセット
                if (input_status == InputStatus.reset)
                {
                    timer_status = TimerStatus.ready;
                    InitDisplay();
                    break;
                }
                // もう一度一時停止が押されたら、現時点から再開する
                if (input_status == InputStatus.pause)
                {
                    start_time = Time.time + now_time;
                    timer_status = TimerStatus.running;
                    break;
                }
                // 間違えて止めた時用に再開ボタンを用意する
                if (input_status == InputStatus.resume)
                {
                    timer_status = TimerStatus.running;
                    break;
                }
                break;
            case TimerStatus.finished:
                // 数字を黄色にしておく
                ChangeColor("#EEB033FF");

                // 停止中にF17が押されたらリセット
                if (input_status == InputStatus.reset)
                {
                    timer_status = TimerStatus.ready;
                    InitDisplay();
                    break;
                }
                // 間違えて止めた時用に再開ボタンを用意する
                if (input_status == InputStatus.resume)
                {
                    timer_status = TimerStatus.running;
                    break;
                }
                break;
        }
        // 時間修正用オフセットを変更する。タイマーも一回描画する
        switch (input_status)
        {
            case InputStatus.up_1sec:
                ChangeOffset(1f);
                break;
            case InputStatus.down_1sec:
                ChangeOffset(-1f);
                break;
            case InputStatus.up_10sec:
                ChangeOffset(10f);
                break;
            case InputStatus.down_10sec:
                ChangeOffset(-10f);
                break;
            case InputStatus.up_1min:
                ChangeOffset(60f);
                break;
            case InputStatus.down_1min:
                ChangeOffset(-60f);
                break;
            case InputStatus.up_10min:
                ChangeOffset(600f);
                break;
            case InputStatus.down_10min:
                ChangeOffset(-600f);
                break;
            case InputStatus.up_1hour:
                ChangeOffset(3600);
                break;
            case InputStatus.down_1hour:
                ChangeOffset(-3600);
                break;
            case InputStatus.updown_reset:
                ChangeOffset(-offset_time);
                break;
        }

        input_status = InputStatus.ready;
    }

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

    // 文字のカラーをすべて変更する
    private void ChangeColor(string color_hex)
    {
        if (color_hex == null)
        {
            return;
        }

        if (ColorUtility.TryParseHtmlString(color_hex, out var color))
        {
            // 変換成功
            hour1image.color = color;
            hour2image.color = color;

            minute1image.color = color;
            minute2image.color = color;

            second1image.color = color;
            second2image.color = color;
        }
    }

    private void ChangeOffset(float offset)
    {
        offset_time = offset_time + offset;
        SetTime(now_time + offset_time);
    }

    void InitDisplay()
    {
        start_time = 0.0f;
        offset_time = 0.0f;
        now_time = 0.0f;

        SetTime(0.0f);
    }

    void SetTime(float ftime)
    {
        if (ftime < 0.0f)
        {
            ftime = ftime * -1;
            // 数字を赤くしておく
            ChangeColor("#FF0000FF");
        }
        else
        {
            // 数字を白にしておく
            ChangeColor("#FFFFFFFF");
        }

        ftime = ftime * 10;
        int itime = Mathf.FloorToInt(ftime);

        int hour = itime / 36000;
        int hour1 = hour / 10;
        int hour2 = hour - hour1 * 10;
        hour1image.sprite = number[hour1];
        hour2image.sprite = number[hour2];

        itime = itime - hour * 36000;

        int minute = itime / 600;
        int minute1 = minute / 10;
        int minute2 = minute - minute1 * 10;
        minute1image.sprite = number[minute1];
        minute2image.sprite = number[minute2];

        itime = itime - minute * 600;

        int second = itime / 10;
        int second1 = second / 10;
        int second2 = second - second1 * 10;
        second1image.sprite = number[second1];
        second2image.sprite = number[second2];
    }
}

ほぼ CountUpMain.cs と同じだが、running 中の now_time の計算および一時停止からの再開時の start_time の計算はマイナスになるので注意

カウントダウンが 0 になってしまったら文字が赤くなる

ストップウォッチとアイコンを変えてわかりやすく

カウントダウンタイマーも正しく動いていることを確認しておく

バッチリ!

おつかれさまでした

次は右下にマウス入力の UI を作ります。これだけあれば通常の操作は大丈夫かな?


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