Let's Enjoy Unreal Engine

Unreal Engineを使って遊んでみましょう

UEFN Verseでイベントドリブンな爆破スイッチを作ってみる

前回に引き続き今回もUEFNとVerseネタです。

前回はVerseを使ってTickモドキを実装しました。
unrealengine.hatenablog.com

今回はゲームを作る上で、とても重要となるイベントドリブンの仕組みをしっかりと理解していきたいと思います。イベントドリブンとはゲームの中で何かしらの出来事(イベント)が発生した時に、その発生したことをどこかへと通知する仕組みです。

このイベントドリブンが出来ていればあらゆるゲームの仕組みを実装することがしやすくなります。今回はUEFNの仕掛けとVerseを組み合わせて、爆破スイッチを作ってみたいと思います。

Verse Deviceの追加と仕掛けの準備

まずはいつものようにVerseスクリプトコードを追加します。Verse ExplorerからVerse Deviceを追加しておきましょう。今回は『explode_button_device』というVerse Deviceを作成しました。

次に『ボタン』仕掛けをFortniteのDevicesフォルダーからD&Dして配置します。ボタンと一緒に特に意味はありませんがそのままだとボタンが浮いてしまうので、キューブのスタティックメッシュを配置しています。

更に同じ場所にある『爆破仕掛け』を複数個配置しておきます。今回は適当に4つほど配置。

一旦これで配置するアクターの準備が完了しました。それではVerseのコードをみていきましょう。

Verseから仕掛けを参照する

次にVerseのコードから仕掛けを参照できるようにしてみましょう。

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

explode_button_device := class(creative_device):
    # ボタン仕掛け
    @editable
    ButtonDevice : button_device = button_device{}

    # 爆破仕掛けの配列
    @editable
    ExplosiveDevices : []explosive_device = array{}

プロパティ(変数)に "@editable" をつけることで、UEFNから参照をすることができるようになります。今回は『button_device(ボタン仕掛け)』クラスと『explosive_device(爆破仕掛け)』を作成し、後者は複数扱いたいので、配列を作成するように"array{}"宣言がついています。

これでUEFN内から参照できるようになります。UEFNに戻って『Verseコードをビルド』しましょう。レベルに存在する『explode_button_device』を選択し、それぞれのプロパティに仕掛けを設定します。配列の場合は一度『+』ボタンを4回押してから追加を行います。

『ボタン』仕掛けには自由に設定を変えておいても問題ありません。今回は『インタラクト時間』、『インタラクトテキスト』、『起動可能回数』を修正しています。

これで全ての参照と設定が完了しました。

イベントをSubscribe(バインド)する

次はイベントのバインド、通称"Subscribe"を行います。SubscribeはVerseにおいて、コールバック関数を登録するための汎用的なインターフェース関数となっており、void型を戻り値とする関数であれば、何でも登録が可能です。

    OnBegin<override>()<suspends>:void=
        # ボタン仕掛けにOnInteractedButton関数をSubscribe(バインド)する
        ButtonDevice.InteractedWithEvent.Subscribe(OnInteractedButton)

    OnInteractedButton(Agent:agent):void= 
        Print("On Interacted")

このVerseコードを実行すると、ゲームスタート後に『ボタン』仕掛けに近づくと、自動的にこのようなメッセージが表示され、Eキーを押すことでPrintが出力されるはずでず。

これでイベントのSubscribeが完了していることが確認できました。

爆破させる

それでは最後にスイッチ(ボタンですが)を押してバレルを爆破させてみましょう。爆破させるためのVerseコードは以下です。

    OnInteractedButton(Agent:agent):void= 
        Print("On Interacted")
        # spawn式で非同期にExplodeBarrels関数を呼び出す
        spawn{ExplodeBarrels(ExplosiveDevices, Agent)}

    ExplodeBarrels(Barrels:[]explosive_device, Agent:agent)<suspends>:void =
        # ExplosiveDevices配列をforで回し、Barrelをひとつずつ爆破
        # ExplodeBarrels関数は非同期なので、Sleep関数で1秒ごとにループする
        for(Barrel : Barrels):
            Print("On Explode")
            Barrel.Explode(Agent)
            Sleep(1.0)

少しコードが多いので、ゆっくりみていきましょう。

まず実際に爆発を行う、"ExplodeBarrels"関数がありますがこの関数内で実際に爆破処理を行います。この関数を呼び出すために、"OnIteractedButton"関数、つまりSubscribeされたイベントのコールバック関数ですがこの中は直接関数をコールするためではなく、spawn式を使って関数呼び出しを行っています。

spawn式は前回の記事でも解説したように非同期関数呼び出しを行うことが可能となる式です。なぜ非同期関数呼び出しを行うかは後程解説します。"ExplodeBarrels"関数は非同期なので、suspendsエフェクト指定子も必要になります。

次に"ExplodeBarrels"関数の中身ですが、for式を使って引数Barrelsをひとつずつループして処理していきます。Barrelは"Explode"関数を呼び出すことで、実際に爆破処理を行います。最後にSleep関数が1秒入るようになっています。ここで改めて"ExplodeBarrels"関数がspawn式を使って非同期で実行されていることが活きてきます。

爆発は1秒感覚でfor式が実行されるため、順番に爆破されていくシーケンスがループの中で自然と出来ていることに感動です。これはまさしく非同期関数だからこその便利な点と言えるでしょう。

爆破完了時にエンドゲームを呼び出す

最後に爆破が完了したら『エンドゲームの仕掛け』を使って、ゲームを終了させましょう。

Verse Deviceにも『end_game_deveice』の参照をもたせるようにコードを修正します。

    # エンドゲーム仕掛け
    @editable
    EndGameDevice: end_game_device = end_game_device{}

スクリプトをビルド後に、UEFN上からEndGameDeviceのプロパティにもしっかり参照設定してあげるのも忘れないようにしてください。これで全てのVerseコードが完成しました。これを実際に実行すると以下の動画のようになります。

最後にVerseコードの完成版です。ぜひ実際に動かして試してみてください!

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

explode_button_device := class(creative_device):
    # ボタン仕掛け
    @editable
    ButtonDevice : button_device = button_device{}

    # 爆破仕掛けの配列
    @editable
    ExplosiveDevices : []explosive_device = array{}

    # エンドゲーム仕掛け
    @editable
    EndGameDevice: end_game_device = end_game_device{}

    OnBegin<override>()<suspends>:void=
        # ボタン仕掛けにOnInteractedButton関数をSubscribe(バインド)する
        ButtonDevice.InteractedWithEvent.Subscribe(OnInteractedButton)

    OnInteractedButton(Agent:agent):void= 
        Print("On Interacted")
        # spawn式で非同期にExplodeBarrels関数を呼び出す
        spawn{ExplodeBarrels(ExplosiveDevices, Agent)}

    ExplodeBarrels(Barrels:[]explosive_device, Agent:agent)<suspends>:void =
        # ExplosiveDevices配列をforで回し、Barrelをひとつずつ爆破
        # ExplodeBarrels関数は非同期なので、Sleep関数で1秒ごとにループする
        for(Barrel : Barrels):
            Print("On Explode")
            Barrel.Explode(Agent)
            Sleep(1.0)

        # 爆破完了時にエンドゲームを呼び出す
        EndGameDevice.Activate(Agent)