LoginSignup
0
0

【UEFN】Verseでシンプルなタイマーを作ってみた

Last updated at Posted at 2023-12-09

背景

実はタイマー仕掛けというものが元々用意されています。
image.png
しかし便利がゆえにごちゃごちゃしすぎる怖さがありました。
(設定できる項目がこんなにたくさん。。。)
image.png
image.png
image.png
そこで今回はシンプルなタイマーを作ろうということになりました。

実装

全体コード

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に書いてしまうと視認性が悪くなるので別のクラスにしました。

デバイス関連

配置
image.png

image.png
インスペクタでの設定
image.png

  • edit_countで制限時間設定できる
  • is_count_upでカウントアップかカウントダウンか設定できる
    image.png
  • simple_timerとbillboardを結び付ける

コードについて

今回初めてブログでeventを利用してみました。はじめはlistenable型が用意されているかなと持ったのですが使えなかったのでeventを使う形になりました。

tutiyaさんの記事が分かりやすくとても参考になりました。
https://zenn.dev/t_tutiya/articles/b413668767a612

loop文内でeventをawaitすることでSubscribeと似た動作ができる感じになっています。

  1. Eventのsignalが呼ばれる
  2. awaitが解除される
  3. それ以下の処理が行われる
  4. loopの初めに戻る
  5. awaitで次のsignalを待つ
 loop:
        timer.StartEvent.Await()
        Print("Timer Start")

まとめ

今回はガッツリコードを書いていきました。int型から時間表示に変換するコードを考えるのが少しつらかったです(笑)。今後はevent型はよく使っていくことになりそうなので覚えていて損は無さそうです!

余談

この度UEFN/Verseに関するオープンコミュニティサーバーを建ち上げました。ちょっとでも興味があれば奮ってご参加くださいませ。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0