[UEFN][verse]グローバル変数を使いたい[3]weak_mapとsession
軽くお知らせ
まだ先の話ですが、いずれVerseの記事は土屋がメインでやっているはてなブログの方にアップしていく予定でいます(シリーズで書いている物はキリの良い所までzennに上げます)。
今回はVerseでグローバル変数を使いたいという話の3回目です。前回はこちら(タイトル変えました)。
weak_map
v25.20から、weak_map
というコンテナ型が追加されました。
日本語ドキュメントにはまだweak_map
の記載がありません[1]。
weak_map
はグローバル変数として使用する事が許可されている変数型です。また、ラウンドを超えて継続的に値を保持する機能を持っています(ただし、こちらについてはv25.40現在は正しく機能していません)。
今回はこのweak_mapについて現在分かっている事をまとめました。
基本説明
まず、weak_map
の機能について確認します。weak_map
は、組み込みのコンテナ型であるmap
のスーパータイプです。
「スーパータイプ(supertype)」は、クラスにおける「スーパークラス(≒親クラス≒派生元クラス)」の型(type)バージョンだと考えて問題ありません。詳しくはこちらの記事を参照。
weak_map
は「弱い(weak)map」という名前の通り、mapの機能が限定された型です[2]。
weak_map
は、map
と比較して以下が異なっています。
-
Length
メンバを持たず、要素数を確認出来ない。- ←正確には、
map.Length()
は拡張メソッドのため、map
でしか使用できません。
- ←正確には、
- イテレート出来ない
- ←
For
式で巡回する事ができません
- ←
-
ConcatenateMaps()
関数を適用出来ない- ←
ConcatenateMaps()
関数はmap
同士を結合して新しいmap
を定義する関数です。
- ←
- グローバル変数として使用出来る
- ←後述します
初期化と使用
実際の使い方は以下のようになります(公式ドキュメントから引用)。
ExampleFunction():void=
var MyWeakMap:weak_map(int, int) = map{}
if:
set MyWeakMap[0] = 1
then:
if (Value := MyWeakMap[0]):
Print("Value of map at key 0 is {Value}")
ここでは、キーと値がどちらもint
型のweak_map
であるMyWeakMap
変数を定義し、値の設定と取得をしています。
weak_map
の型宣言はパラメータ型クラスと同じ仕様になっていて、map
の型宣言とは書き方が異なる点に注意が必要です。念のためmap
の型宣言もおさらいしておきましょう。下記はキーと値がどちらもstring
のmap
になります(公式ドキュメントから引用)。
ExampleMap : [string]string = map{"a" => "apple", "b" => "bear", "c" => "candy"}
また、初期化の際には=map{}
を使います。これはweak_map
がmap
のスーパータイプである事によります。なのでこういう形の初期化もできます。
MyWeakMap:weak_map(int, int) = map{0 => 2}
一方、以下の様に右辺でweak_map
を生成するような初期化はできません。
MyWeakMap:weak_map(int, int) = weak_map{}
なぜこれが出来ないのかは想像しかできませんが。恐らくweak_map
は実際にはカスタムクラスで、純粋な型として定義されていないのだと思います。後述するようにweak_map
は処理系の都合で用意された部分があるため、言語仕様に組み込んでいないのでしょう[3]。
グローバル変数としての利用(session型の使用)
weak_map
は、現時点のVerseにおいて唯一、moduleスコープで定義する事が出来る型です。つまり、weak_map
はグローバル変数として使用できる型であり、敢えて機能が制限されたmap
を使う理由はここにあります[4]。
前回、グローバル変数的な使い方が出来る唯一の方法としてタグを使う手法を紹介しました。weak_map
型を使えばよりシンプルにグローバル変数へのアクセスが可能になります。
グローバル変数として定義する場合は以下の様に記述します(公式ドキュメントから引用)。
var GlobalInt:weak_map(session, int) = map{}
ExampleFunction():void=
X := if (Y := GlobalInt[GetSession()]) then Y + 1 else 0
if:
set GlobalInt[GetSession()] = X
Print(“{X}”)
このコードではキーがsession、値がintのweak_mapであるGlobalInt変数をモジュールスコープに定義しています。
モジュールスコープに定義する場合、キーはsessionでなければならず、それ以外の型を指定した場合は下図のようにコンパイルエラーになります。
session
というのはverse.digest.verse
ファイルで定義されている組み込みの<unique>
クラスで、今現在実行している「セッション(Session)」を表すunique
なインスタンスを指定します。
この「セッション」という用語はどうも定義がないっぽいのですが、「ある島がサーバー上でインスタンス化してから、その島から全てのユーザーがいなくなりインスタンスが削除されるまで」を1セッションと考えて良いだろうと思います。
session
インスタンスはGetSession()
関数を通してのみ取得可能です。同一セッション内であれば常に同じインスタンスが返ると考えて良いでしょう。この仕組みにより、現在のセッションに割り当てられたグローバル変数にアクセスできるわけです。
仕組みを想像してみる
何故グローバル変数にアクセスするためにsession
を指定しなければならないのかですが、仕様から想像するに、モジュールスコープ(≒グローバル)に配置されたweak_map
は、サーバー上でインスタンス化されている全てのセッションで共有されているんじゃないかと思います。定数も共有出来るのでメモリ効率良さそうだし。
そして、その共有されているweak_map
から、自身のセッション用の値だけをアクセスする(≒他のセッションに干渉しない)ために、session
変数をキーにするのが強制されているのかなと思います。
(工事中)ラウンドを超えた永続化は現時点では上手く動かない模様
※土屋自身がラウンドをまたぐコードをまだ書いていないため、この稿は未検証です。ご注意ください。
(23/08/29 時間が無くてここは作業中)
お知らせ
verse言語とUEFNの記事を他にも書いているので御覧下さい。
最後まで読んで頂きありがとうございました。この記事がお役に立てたようであれば、是非LIKEとフォローをお願いします(今後の執筆のモチベーションに繋がります)。
#Verse #UEFN #Fortnite #Verselang #UnrealEngine
宣伝
「Unityシェーダープログラミングの教科書」シリーズ1~5をBOOTHで頒布中です。
Discussion