背景
実はタイマー仕掛けというものが元々用意されています。
しかし便利がゆえにごちゃごちゃしすぎる怖さがありました。
(設定できる項目がこんなにたくさん。。。)
そこで今回はシンプルなタイマーを作ろうということになりました。
実装
全体コード
simple_timer_device
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
simple_timer_device := class(creative_device):
@editable
edit_count:int=0
@editable
var is_count_up:logic=true
var nowCount:int=0
var isPlay:logic=false
StartEvent<public>:event() = event(){}
StopEvent<public>:event() = event(){}
CountEvent<public>:event(int) = event(int){}
FinishEvent<public>:event() = event(){}
Start()<suspends>:void=
set isPlay=true
StartEvent.Signal()
if (is_count_up=true):
set nowCount = edit_count
spawn { CountUp() }
else:
set nowCount = 0
spawn { CountDown() }
Print("Count: {nowCount}")
Stop()<suspends>:void=
set isPlay=false
StopEvent.Signal()
Print("Count: {nowCount}")
Play()<suspends>:void=
set isPlay=true
if (is_count_up=true):
spawn { CountUp() }
else:
spawn { CountDown() }
Print("Count: {nowCount}")
# 一度タイマーを止めないと切り替わりません
Switch(isCountUp:logic)<suspends>:void=
set is_count_up=isCountUp
Print("Count: {nowCount}")
CountDown()<suspends>:void=
set nowCount = edit_count
loop:
Sleep(1.0)
set nowCount-=1
CountEvent.Signal(nowCount)
if (nowCount <= 0):
set isPlay=false
FinishEvent.Signal()
break
else if (isPlay=false):
Print("Stop")
break
Print("Count: {nowCount}")
CountUp()<suspends>:void=
set nowCount = 0
loop:
Sleep(1.0)
set nowCount+=1
CountEvent.Signal(nowCount)
if (nowCount >= edit_count):
set isPlay=false
FinishEvent.Signal()
break
else if (isPlay=false):
Print("Stop")
break
Print("Count: {nowCount}")
構成について
今回はシンプルなタイマーということでスタート時とカウント時、ストップ時、終了時のイベントのみが見れるようにしました。またオプションとしてカウントアップなのかカウントダウンなのかも指定できるようにしてみました。さらに本家では60時間までしかはかれなかったのでそれ以上にの時間がはかれるようにもしてみました。(99時間まで行けます。)
利用する方法
やること
billboardに現在の時間を映してみます。
動画
sample_timer_presenter_device
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
sample_timer_presenter_device := class(creative_device):
@editable
billboard:billboard_device= billboard_device{}
@editable
timer:simple_timer_device=simple_timer_device{}
converter:seconds_to_time_converter=seconds_to_time_converter{}
timeMessage<localizes>(msg:string):message="{msg}"
OnBegin<override>()<suspends>:void=
timer.Start()
spawn { StartAwaitHandler() }
spawn { CountAwaitHandler() }
spawn { StopAwaitHandler() }
spawn { FinishAwaitHandler() }
StartAwaitHandler()<suspends>:void=
loop:
timer.StartEvent.Await()
Print("Timer Start")
CountAwaitHandler()<suspends>:void=
loop:
timeValue := timer.CountEvent.Await()
if (timeString := converter.ConvertToTime[timeValue]):
billboard.SetText(timeMessage(timeString))
else:
billboard.SetText(timeMessage("00:00:00"))
StopAwaitHandler()<suspends>:void=
loop:
timer.StopEvent.Await()
Print("Timer Stop")
FinishAwaitHandler()<suspends>:void=
loop:
timer.FinishEvent.Await()
billboard.SetText(timeMessage("FINISH"))
Print("Timer Finish")
タイマーとbillboardを紐づけるクラスになります。
seconds_to_time_converter
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
seconds_to_time_converter := class():
ConvertToTime(sec:int)<transacts><decides>:string=
if ( sec >= 3600):
hours := sec / 3600
minutes := Mod[sec, 3600] / 60
seconds := Mod[sec, 3600]
S := Mod[seconds, 60]
H:int=Ceil(hours)
M:int=Ceil(minutes)
if (H < 10):
if (M < 10):
if (seconds < 10):
timeString := "0{H}:0{M}:0{seconds}"
return timeString
else:
timeString := "0{H}:0{M}:{seconds}"
return timeString
else:
if (seconds < 10):
timeString := "0{H}:{M}:0{seconds}"
return timeString
else:
timeString := "0{H}:{M}:{seconds}"
return timeString
else:
if (M < 10):
if (seconds < 10):
timeString := "{H}:0{M}:0{seconds}"
return timeString
else:
timeString := "{H}:0{M}:{seconds}"
return timeString
else:
if (seconds < 10):
timeString := "{H}:{M}:0{seconds}"
return timeString
else:
timeString := "{H}:{M}:{seconds}"
return timeString
else if (sec < 3600 and sec >= 60):
minutes := sec / 60
seconds := Mod[sec, 60]
M:int=Ceil(minutes)
if (M < 10):
if (seconds < 10):
timeString := "00:0{M}:0{seconds}"
return timeString
else:
timeString := "00:0{M}:{seconds}"
return timeString
else:
if (seconds < 10):
timeString := "00:{M}:0{seconds}"
return timeString
else:
timeString := "00:{M}:{seconds}"
return timeString
else if (sec <= 59):
seconds := sec
if (seconds < 10):
timeString := "00:00:0{seconds}"
return timeString
else:
timeString := "00:00:{seconds}"
return timeString
else:
timeString := "00:00:00"
return timeString
こちらもっと最適化できそうなコードではありますがただのint型
の値から00:00:00
というstring型に変換するクラスになります。変換するクラスをpresenterに書いてしまうと視認性が悪くなるので別のクラスにしました。
デバイス関連
コードについて
今回初めてブログでevent
を利用してみました。はじめはlistenable型
が用意されているかなと持ったのですが使えなかったのでevent
を使う形になりました。
tutiyaさんの記事が分かりやすくとても参考になりました。
https://zenn.dev/t_tutiya/articles/b413668767a612
loop文内でeventをawaitすることでSubscribeと似た動作ができる感じになっています。
- Eventのsignalが呼ばれる
- awaitが解除される
- それ以下の処理が行われる
- loopの初めに戻る
- awaitで次のsignalを待つ
loop:
timer.StartEvent.Await()
Print("Timer Start")
まとめ
今回はガッツリコードを書いていきました。int型から時間表示に変換するコードを考えるのが少しつらかったです(笑)。今後はevent
型はよく使っていくことになりそうなので覚えていて損は無さそうです!
余談
この度UEFN/Verseに関するオープンコミュニティサーバーを建ち上げました。ちょっとでも興味があれば奮ってご参加くださいませ。