[#UEFN][#Verse]UEFNのカスタムイベント実装を考える[5] ペイロードを送信する
前回はこちら
今回は前回の記事で触れなかった「ペイロード」について。
その前に前回説明が不足していた箇所についてフォローアップします。
前回のフォローアップ:空引数とtuple()の関係について
前回の記事は、ヘルパー関数を使えばパラメータを持たない(正確には「要素数ゼロのtuple
型」を持つ)event
オブジェクトを定義出来るという物でした。
ところで、第3回のサンプルコードには以下の様な行があります。Event
定数はevent(tuple())
クラスのインスタンスが定義されています。
Event.Signal()
event.Signal()のインターフェイスは以下の様に定義されています。
Signal<native><override>(Val:t):void
Val
はevent
オブジェクトに渡すペイロードです。「ペイロード」については後述するとして、ここでは関数呼び出しのインターフェイスについて注目します。
先述したように、引数Val
のパラメータ型t
は、ここではtuple()
にパラメタライズされています。しかし関数呼び出しの式はEvent.Signal()
となっており、引数が記述されていません。引数の個数が一致していないのに、なぜコンパイルエラーにならないのでしょうか?
これは、恐らくですが、Verseでは関数呼び出し時の引数群を内部的にtuple
型に格納していると思われ、その結果、引数の無い関数呼び出しも要素数ゼロのtuple
型に変換されるので、インターフェイスが整合するのだと思われます。
この仕様は明言されていないのでいずれも想像の域を出ませんが、引数におけるtuple
の自動展開という仕様があり、そこから類推しています。
フォローアップはここまで。
承前:「ペイロード」という用語について(豆知識)
「ペイロード(Payload.)」とはなんでしょう。IT用語辞典 e-wordsから引用します。
ペイロードとは、有料荷重、有効搭載量、最大積載量、積載物などの意味を持つ英単語。
通信・ネットワークの分野において、送受信されるデータの伝送単位(パケットやデータ
グラムなど)のうち、宛先などの制御情報を除いた、相手に送り届けようとしている正味
のデータ本体のことをペイロードという。
プログラミングでは「通信で送受信するデータ本体」を指してペイロードと言うようです。ちなみに土屋は「このロケットのペイロードはxxトンなので」という文脈で聞いたことがあります。
eventオブジェクトのペイロード
event
オブジェクトを使うと、あるタスクの中でAwait()
を実行して待機状態に移行させ、その後で外部からSignal()
を実行してそのタスクの待機状態を解除するという制御が行えます。
この時、Signal()
の引数に値を渡すと、その値をAwait()
の戻り値として受け取る事ができます。挙動としては外部からタスクに向かって値を送信しているように見えます。なので、この送信する値の事を「ペイロード」と呼んでいます。
イベントクラスのインターフェイスを再確認しましょう。
event<native><public>(t:type)<computes> := class(signalable(t), awaitable(t)):
Await<native><override>()<suspends>:t
Signal<native><override>(Val:t):void
ペイロードとして渡す値の型はevent
クラスのインスタンスを生成する時にパラメータで指定します。上記を見ると、パラメータ型t
がSignal()
の引数の型とAwait()
の戻り値の型にそれぞれ設定されているのが分かります。
ペイロードを使ったイベント処理を実装する
では実際にペイロードを使うイベント処理を実装してみましょう。
前回の記事を書いてる最中に「このペイロードの仕組みを使えば、最初のサンプルコードでagent
を取り回していた面倒な箇所をシンプルに書けるのでは?」と気づき、実践してみました。
サンプルコードはボタンデバイスが押下されたタイミングでスイッチデバイスのON/OFFをトグルするという物でした。スイッチをトグルするのはswitch_device.ToggleState()
メソッドを実行すれば良いのですが、このメソッドは引数に実行者となるagent
が必要です。その為、呼び出し元からタスクに向かってagent
への参照を通知する必要があったわけです。これをペイロードで行います。
以下、以前のサンプルコードとの差分箇所について、処理順に解説します。完全なサンプルコードは最後にアップしています。
Event<public>:event(agent) = event(agent){}
event
クラスのパラメータにagent
を設定してインスタンスを生成、Event
定数を定義します。以前はevent()
ヘルパー関数呼び出しでしたが、今回は直接クラス生成しています(ややこしいですね!)。
Subscribe(CustomEvent: custom_event)<suspends>:void=
Agent := CustomEvent.Event.Await()
生成されたタスクをevent.Await()
で待機状態に移行します。右辺の評価時に待機状態に入っているので、この段階では式は値を返しておらず、Agent
定数も(まだ)定義されていない点に注意して下さい。
Emit<public>(Agent:agent):void=
Event.Signal(Agent)
さて、ボタンが押されると、格納されているcustom_event
オブジェクトそれぞれについてcustom_event.Emit()
が呼びだされます。以前はここでメンバ変数にAgent
を格納していましたが、今回はSingal()
メソッドにAgent
を渡します(逆にメンバ変数は用意していません)。
Agent := CustomEvent.Event.Await()
CustomEvent.Switch.ToggleState(Agent)
ぐるりと一周してきました。Signal()
の実行によってタスクの待機状態は解除されます。Signal()
の引数に設定したagent
オブジェクト(これがペイロードです)は、Await()
の戻り値として送信され、Agent
定数に定義されます。この値を使ってswitch_devince.ToggleState()
を実行します。
というわけで、agent
を取り回していた処理を、奇麗にペイロードに置き換える事が出来たのでした。
サンプルコード
using { /Verse.org/Simulation/Tags }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Verse.org/Simulation }
using { /Fortnite.com/Devices }
custom_event := class():
Switch :switch_device
Event<public>:event(agent) = event(agent){}
Emit<public>(Agent:agent):void=
Event.Signal(Agent)
pub_test_device := class(creative_device):
@editable MyButton : button_device = button_device{}
@editable Switches : []switch_device = array{switch_device{}}
var SwitchEvents: []custom_event = array{}
Subscribe(CustomEvent: custom_event)<suspends>:void=
Agent := CustomEvent.Event.Await()
CustomEvent.Switch.ToggleState(Agent)
spawn{Subscribe(CustomEvent)}
OnBegin<override>()<suspends>:void=
MyButton.InteractedWithEvent.Subscribe(HandleButtonInteraction)
for:
Switch : Switches
do:
EventObj : custom_event = custom_event{Switch := Switch}
set SwitchEvents += array{EventObj}
spawn{Subscribe(EventObj)}
HandleButtonInteraction(Agent : agent) : void =
for:
SwitchEvent : SwitchEvents
do:
SwitchEvent.Emit(Agent)
おわりに
元のサンプルコードを書いていた時「なんでagent
を上手い事取り回せないんだ!」と憤慨していたのですが、方法はちゃんと用意されていたという話でしたすみません。
ここまで来たらevent
オブジェクト自体をスクラッチで実装出来るんじゃないかという気もします(出来ない気もします)。次回はそれに挑戦して上手く行けばイベント編は完結の予定(上手く行かなければ今回で完結)。
続き
お知らせ
verse言語とUEFNの記事を他にも書いているので御覧下さい。
最後まで読んで頂きありがとうございました。この記事がお役に立てたようであれば、是非LIKEとフォローをお願いします(今後の執筆のモチベーションに繋がります)。
#Verse #UEFN #Fortnite #Verselang #UnrealEngine
宣伝
「Unityシェーダープログラミングの教科書」シリーズ1~5をBOOTHで頒布中です。
Discussion