[UEFN][verse]グローバル変数を使いたい[4]weak_mapはラウンドを超えて値を持てない事が確定
weak_map
では、ラウンドを超えて値を保持する事は出来ない事が確定したので今回はそもそもこれがどういう問題だったのかの話です。weak_map
自体やsession
クラスについては前回記事を参照して下さい。
weak_mapについて
グローバル変数として振る舞えるweak_map
は、v25.00から追加された仕様で、「session-local globals(セッション単位のグローバル変数)」が作れるという触れ込みでした。
Verseではグローバル変数の定義が許されず、これまではタグを使って擬似的に実現するしかなかったので、この機能はコミュニティに歓迎されました。
特に注目されたのは「セッション単位(session-local)」であるという点です。多くの人が「これはつまり、ラウンドを超えて値を持ち越せるという事では?」と考えました。
以下、セッションとラウンドについて説明します。
セッションについて
クリエイト島において、セッションとは「島のインスタンスが生成されてからそれが削除されるまで」を意味します。インスタンスというのは、サーバー上に用意されたその島のためのメモリ領域の事を指すと考えて良いでしょう。
ソロ限定のマップであれば「1人がゲームを開始して、終了するまで」が1セッションになります。出入り自由のマップであれば、「誰かがゲームを開始して、その島からプレイヤーがいなくなるまで」が1セッションになります。
同じ島について複数のインスタンスがサーバーに用意されることも普通にあります。設定にも寄りますが、4人限定マップであれば、既に4人いる時に5人目がゲームを始めようとしたら新しいインスタンスが生成されますし、プライベートゲームはそれぞれが独立したインスタンスになります。
ラウンドについて
セッションとは別に、クリエイト島には「ラウンド」という単位があります。これは「10点先取」「3分以内」などのルールによってゲームを区切る物で、チーム戦をするマップでは大抵採用されています。
ルールの適用によってラウンドが終了すると、新しいラウンドが始まります。この時、ラウンド毎のポイントの記録などは記録されますが[1]、島やプレイヤーの状態はリセットされます。
プレイヤーの状態もリセットされるというのは、例えばチーム編成やプレイヤーのクラス設定が次ラウンドに引き継がれないという事です。複数ラウンドからなるゲームをデザインしたい場合、この制限はなかなかに厳しいと思えます。クリエイティブユーザーは1.0時代からこの制限への対応方法に頭を悩ませてきたようです。
weak_mapの登場と疑念
そこに登場したのがweak_map
でした。「「セッション単位のグローバル変数」なんだから、ラウンドを超えて値が保持される筈だ!」という感じで、ユーザーにとって待望の機能だったわけです。
ところがこのweak_map
、当初からラウンドを超えての値の保持が上手く行かないと、公式フォーラムで報告が上がっていました[2]。ラウンド単位でのグローバル変数としては機能するものの、ラウンドを超えて状態を保持する(以下これを「クロスラウンドステート」と呼びます)処理を正しく実装出来た人は現れませんでした。
とはいえ、UEFNはベータリリース中ですし、weak_map
に限らず、当時の[3]Verseはちょっとした事でバグを踏んだりクラッシュしたりしていたので、weak_map
についても「今はまともに動かないが、いずれは使えるようになるだろう」と考えられていたのでした。
改めてこうして文章にすると「なにを悠長な事を言っているんだ?」という気もしますが、実際に現時点のUEFN/Verseの完成度はこれくらいなのです。なにせベータリリース中ですから!(実用にならないという意味ではないんですよ。VerseもUEFNも非常に将来性のある開発環境です)。
APIドキュメントの更新
コミュニティがweak_map
をクロスラウンドステートで動作させる方法を見つけられずにいる中、前回のv26.00→v26.10アップデートの際に、session
クラスとGetSession()
関数のAPIのコメントが更新されている事にユーザーが気付きました。以下GetSession()
について、新旧ドキュメントの対訳を示します。
v26.00
# Returns the `session` corresponding to the current server instance.
# The result can be used with `weak_map` to mimic global variables in other languages.
# 現在のサーバーインスタンスに対応する`session`を返す。
# この結果を`weak_map`と共に使用すれば、他の言語におけるグローバル変数を模倣できる。
GetSession<native><public>()<varies>:session
v26.10
# Returns the `session` corresponding to the current round.
# The result can be used with `weak_map` to implement global variables.
# Note: may be changed in a future release to return a single instance per game.
# Round-local behavior should not be relied upon.
# 現在のラウンドに対応する`session`を返す。
# この結果を`weak_map`と共に使用すればグローバル変数を実装できる。
# 注意: 将来のリリースでは、ゲームごとの単一インスタンスを返すように変更される可能性があります。
# ラウンドローカルな挙動であると想定するべきではありません。
GetSession<native><public>()<varies>:session
コメントを比較すると、GetSession()
が返すsession
が対応する物が「現在のサーバーインスタンス(v26.00)」から「現在のラウンド(v26.10)」に変更されています。後者の場合、「session
はラウンド単位である(つまり、クロスラウンドステートは動かない)」と解釈できます。
このコメントの変更は、v26.10パッチノートには記載されていませんでした。そのためコミュニティ(のごくごく一部)でも「クロスラウンドステートは元々まともに動いて無かったが、今回正式に使えない物と確定させたのでは?」「いやいや単に古いドキュメントが誤ってロールバックしたんじゃないか?」と議論になりました。
最終的に、公式スタッフが決定打となる投稿を(コレを書いている9/28に)行いました。
原文:
This is unfortunately intended. Use of session as a key in a module-scoped var weak_map may eventually gain back support for cross-round state (hence the scary “Note: …” part of the doc), but it was removed in 26.10. To protect against a behavior change (when cross-round state is added back), make sure to initialize your state at the beginning of OnBegin in a creative_device, e.g.
私訳:
残念ながらこれは意図されたものです。モジュールスコープのweak_map変数のキーとしてのsession使用時におけるクロスラウンドステートのサポートは、いつかは復活するかもしれませんが(ドキュメントの"Note:..."のパートはそのための物です)、26.10では削除されました。(再び、クロスラウンドステートのサポートが追加された時に)動作の変更に影響を受けないように、creative_deviceのOnBeginの最初に必ず状態を初期化するようにしてください。以下のように。
if:
set GlobalInt[GetSession()] = 0
というわけで、weak_map
はラウンドローカルな用途でのみ使用できる事が公式な仕様となりました。
お知らせ
verse言語とUEFNの記事を他にも書いているので御覧下さい。
最後まで読んで頂きありがとうございました。この記事がお役に立てたようであれば、是非LIKEとフォローをお願いします(今後の執筆のモチベーションに繋がります)。
#Verse #UEFN #Fortnite #Verselang #UnrealEngine
宣伝
「Unityシェーダープログラミングの教科書」シリーズ1~5をBOOTHで頒布中です。
Discussion