👋

[UEFN][verse]グローバル変数を使いたい[4]weak_mapはラウンドを超えて値を持てない事が確定

2023/09/28に公開

weak_mapでは、ラウンドを超えて値を保持する事は出来ない事が確定したので今回はそもそもこれがどういう問題だったのかの話です。weak_map自体やsessionクラスについては前回記事を参照して下さい。

https://zenn.dev/t_tutiya/articles/c5569cc9698ea7

weak_mapについて

グローバル変数として振る舞えるweak_mapは、v25.00から追加された仕様で、「session-local globals(セッション単位のグローバル変数)」が作れるという触れ込みでした。

https://dev.epicgames.com/documentation/en-us/uefn/25.00-release-notes-in-unreal-editor-for-fortnite#language

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に)行いました。

https://forums.unrealengine.com/t/major-weak-map-getsession-broken-in-26-10/1292369/8

原文:
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の記事を他にも書いているので御覧下さい。
https://zenn.dev/t_tutiya

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

#Verse #UEFN #Fortnite #Verselang #UnrealEngine

宣伝

「Unityシェーダープログラミングの教科書」シリーズ1~5をBOOTHで頒布中です。
https://s-games.booth.pm/

脚注
  1. この辺り自分でやってるわけじゃないので自信無い ↩︎

  2. プリミティブ型は持ち越せるけど、クラスインスタンスは持ち越せないなど謎挙動を起こしていた ↩︎

  3. 今も ↩︎

Discussion