🐥

[UEFN][verse] UEFNのカスタムイベント実装を考える[1] Await()の使い方

2023/07/10に公開

以前、イベントハンドラをカスタム実装[1]しました。
https://zenn.dev/t_tutiya/articles/26e2da5a010b39
この発展形として、イベント制御全体のカスタム実装について、今回から(ネタが続いたら)何回かに分けて解説したいと思います。

サンプルコードの実行

今回はひとまず動くコードを見てみます。こちらは公式フォーラムでEp8Scriptという方が考案したお手軽イベントリスナーの実装です。「そもそも「イベントリスナー」ってなによ?」みたいな話は次回以降書ければ良いなと思っています。

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }

event_test_device := class(creative_device):

    @editable Switches : []switch_device = array{switch_device{}}

    OnBegin<override>()<suspends>:void =
        for (Index->Switch : Switches):
            spawn{AwaitSwitchTurnedOn(Switch, Index)}

    AwaitSwitchTurnedOn<private>(Switch:switch_device, Index:int)<suspends>:void =
        loop:
            Switch.TurnedOnEvent.Await()
            Print("Switch[{Index}] turned on")

このコードをビルドし、event_test_deviceをレベルに配置します。
次にスイッチデバイス(switch_device)を幾つかレベルに配置し、それをevent_test_deviceに設定します。以下は3個設定した場合。

この状態で島を実行し、それぞれのスイッチをオンにすると、コンソール(画面上では左端)に、スイッチに対応したインデックス番号を含むテキストが表示されます。

以下動画のサンプル。zennに動画を直接貼れなかったのでtwitterを載せておきます(Youtubeとか使うべきか?)。

https://twitter.com/t_tutiya/status/1678035387896176640?s=20

コードの解説

イベント駆動のコードに慣れていないと、どうして上記の様なコードで、このような挙動が実現できるのか不思議に思うかもしれません。以下詳しく見ていきましょう。

event_test_device := class(creative_device):

    @editable Switches : []switch_device = array{switch_device{}}

スイッチデバイスの配列をクラスメンバSwitchesとして保持し、それをエディタ上から設定出来るようにします。

OnBegin<override>()<suspends>:void =
    for (Index->Switch : Switches):
        spawn{AwaitSwitchTurnedOn(Switch, Index)}

ゲームの開始時に、Switchs配列の個数だけfor式を回します。"Index->Swutch"の部分はverse特有の記法で、SwitchにはSwitchs配列の各要素が、Indexにはループ回数に応じた0からの連番が格納されます。rubyで言うwith_indexですね。

ループ内では、spawn式で新しいタスクを生成し、そのタスク内でAwaitSwitchTurnedOn()メソッドを実行します。それぞれのタスクはここから並行処理になります。

AwaitSwitchTurnedOn<private>(Switch:switch_device, Index:int)<suspends>:void =
    loop:
        Switch.TurnedOnEvent.Await()
        Print("Switch[{Index}] turned on")

AwaitSwitchTurnedOn()メソッドの実装がこちら。ここが割とトリッキーです。loop式の中で、スイッチデバイスのTurnedOnEvent.Await()を実行しています。これにより、実行中のタスクはTurnedOnEvent、すなわちスイッチがオンになるイベントが発生するまで、待機状態になります。

実際の並行処理中の挙動を考える

ここまでの流れを整理します。ゲームの開始時にevent_test_deviceのOnBeginメソッドが呼びだされ、その中でSwitches配列に設定されている全てのスイッチデバイスについてタスクを生成します。それぞれのタスクはTurnedOnEvent.Await()によって待機状態に入ります。これが一連の流れです。

では、実際にプレイヤーがスイッチをオンにした時の事を考えます。スイッチをオンにするとTurnedOnEventが発生します。これによって対応するタスクは待機が終わり、処理を再開します。すなわち、2行目のPrint文を実行し、そしてloopによって一行目に戻り、再びAwait()で待機状態に入ります。

このloopとAwait()のペアによってAwaitSwitchTurnedOn()はスイッチがオンになる度に必要な処理を実行するイベントリスナーとして機能するわけです。面白いですよね。

終わりに

今回紹介したコードは、シンプルすぎて逆に応用がしにくいかもしれません。次回はより細かいイベント制御の方法を考えてみます。

続き

https://zenn.dev/t_tutiya/articles/74f65fbe46dee5


verse言語とUEFNの記事を他にも書いているので御覧下さい。
https://zenn.dev/t_tutiya

最後まで読んで頂きありがとうございました。この記事がお役に立てたようであれば、是非LIKEとフォローをお願いします(今後の執筆のモチベーションに繋がります)。

#Verse #UEFN #Fortnite #Verselang #UnrealEngine

脚注
  1. 「既存の実装を自前で組む」くらい意味 ↩︎

Discussion