![見出し画像](https://assets.st-note.com/production/uploads/images/124616793/rectangle_large_type_2_1c46285dfa5cfe55a826200e05db8abe.png?width=1200)
NDI™で送信するRTAイベントタイマーをUnityで作る #05 カウントダウン編
JAWS PLAYERES では RTA だけでなく企画も行われるので、残り時間が表示できるカウントダウンタイマーも作成しておきます
オブジェクト、背景の設定
ヒエラルキーに CountDown Object を作成。CountDown Background Image も作成。ストップウォッチのときと同じく画像を配置していく
![](https://assets.st-note.com/img/1701425473865-0CjP2QPXFi.png)
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 になってしまったら文字が赤くなる
![](https://assets.st-note.com/img/1701442784878-YZxLyQjA20.png?width=1200)
カウントダウンタイマーも正しく動いていることを確認しておく
![](https://assets.st-note.com/img/1701425877034-LrN4HvV1tX.png?width=1200)
おつかれさまでした
次は右下にマウス入力の UI を作ります。これだけあれば通常の操作は大丈夫かな?