UEFNの公式フォーラムでは、ユーザーからテーマに沿った質問を募集し、それに内部開発スタッフが回答する"Ask Epic"という不定期イベントが開催されています。直近の3月28日に行われたAsk EpicのテーマはVerseでした。
Verseについて直接公式に質問できる機会は限られているため、土屋を含め多くのVerser(海外でのVerse使いの呼称。ただしそこまで流行ってるわけでもない)が日頃感じていた疑問をぶつけました。興味深い回答が沢山あったので、一部をピックアップして翻訳しておきます(数回に分ける予定)。
なお、今回のAsk Epicには、直前に開催されたGDC2024で発表された鳴り物入りの機能「シーングラフ(SceneGraph)」の開発スタッフが参加したこともあり、それに関連する質問も多く寄せられました。今回はシーングラフについては翻訳していませんが、興味ある方は是非当該スレッドをお読みください。
今回は土屋が質問した7項目について翻訳しました。
質問パートの訳は大意、回答パートについても逐語訳にはなっていません。正確な質問/回答内容については原文を参照して下さい。
[言語仕様]choice式について
https://forums.unrealengine.com/t/ask-epic-verse-march-28-10-00-am-et/1765393/92?u=t.tutiya
Q:Simon Peyton Jonesが講演で発表したchoice
式を追加する予定はありますか?
A:残念ながら、新しいVMはMaxVerseを非常に意識して設計されているということ以外、今は時期について多くを示すことはできません。
土屋捕捉
choice
式は、verse言語のメイン設計を担当するサイモン・ペイトン・ジョーンズが、verse言語に将来的に導入される機能として講演で紹介した物です。詳細は不明ですが、土屋はインライン多次元map
展開のような物をやろうとしているんじゃないかと思っています。残念ながら直近で入ることは無さそう。
なお、ここで言及している「新しいVM」は現在Verseが動いているBulePrintVMに変わる専用のVerseVMを指し、「MaxVerse」は当初予定されていた言語機能が全部組み込まれた将来のVerse言語を指します*1。MaxVerseについてはこの後の回答にも何度か登場します。
[バグ]非コンストラクタ関数にコンストラクタ指定子を付与出来るのはバグ
https://forums.unrealengine.com/t/ask-epic-verse-march-28-10-00-am-et/1765393/91?u=t.tutiya https://forums.unrealengine.com/t/ask-epic-verse-march-28-10-00-am-et/1765393/104?u=t.tutiya
Q:クラスを返すだけの関数を呼び出す時にコンストラクタ指定子を付与出来るのはバグですか? 以下のコードはコンパイルできてしまいます。
cat := class: Name:string Sound:string #catインスタンスを返す関数(コンストラクタ関数ではない) MakeCat2(name:string, sound:string):cat = cat: Name := name Sound := sound #実行出来るが、本来ならコンパイルが通ること自体がおかしいように思える。 cat := MakeCat2<constructor>("foo", "boo")
A:これはバグのようです(今バグレポートを作っています)。この場合MakeCat2()
はアーキタイプ初期化としては動作せず、Name
を"foo
"、Sound
を"boo
"としたcat
のインスタンスを生成する、単なる関数の呼び出しのように動作します。
土屋捕捉
この話はちょっと入り組んでまして、そもそも以下の2つの関数の違いはなんなんだという話から始まっています*2。
#コンストラクタ関数。指定子が付与され、戻り値の定義が無い MakeCat1<constructor>(name:string, sound:string) := cat: Name := name Sound := sound #クラスインスタンスを返す関数。指定子はなく、戻り値の定義はある。 MakeCat2(name:string, sound:string):cat = cat: Name := name Sound := sound
記法は違いますが、どちらの関数もcat
インスタンスを返します。もしこの2つの関数に違いが無い場合、コンストラクタ指定子の方の記法いらなくない? と思うわけです。
ただし、「コンストラクタ関数内で実行出来るのはコンストラクタ指定子を付与した関数(つまりコンストラクタ関数)のみ」というルールがあるため、その場合はコンストラクタ関数であるMakeCat1()
しか記述できません。
ところが、現状ではMakeCat2()
にもコンストラクタ指定子を付与すると、コンストラクタ関数内で記述出来てしまう*3事が分かっていて、これはどこまでが仕様なのかと(ごく一部の界隈で)話題になっていたのでした。
今回の回答によって「非コンストラクタ関数にコンストラクタ指定子を付与して呼び出せるのはバグ」と判明したので、このあたりがすっきりしました。
余談ですが「現実問題として、コンストラクタ関数をネストさせる事ってあるの?」というそもそもの疑問があったのですが、最近追加された「データ永続化」という機能を使う時、このケースを多用し得る事が判明しました(結果としてこのバグが顕在化したという話でもあります)。
[バグ]for式の条件部に戻り値のない式を書けないのはバグ
https://forums.unrealengine.com/t/ask-epic-verse-march-28-10-00-am-et/1765393/101?u=t.tutiya
Q:なぜfor
式の条件部に戻り値のない式を書くことができないのですか?
A:正当な理由はありません。これは、現在のfor
構文のパターンマッチ方法に関連するバグです。MaxVerseが想定する式評価とは異なります。
土屋捕捉
下記のように、for
式の条件部で戻り値が無い式を書く事ができない理由に関する質問。バグでした。
test_device := class(creative_device): IncrementSomeVariable()<transacts>:int = 1 OnBegin<override>()<suspends>:void= if: 1=1 S := IncrementSomeVariable() #OK if式では戻り値を取る式を条件に出来る IncrementSomeVariable() #OK if式では戻り値を取らない式を条件に出来る then: for: X := 0..1 S := IncrementSomeVariable() #OK for式では戻り値を取る式を条件に出来る IncrementSomeVariable() #コンパイルエラー for式では戻り値を取らない式を条件に「出来ない」 do:
[バグ]外部ファイルのパラメトリッククラスを参照できないのはバグ
https://forums.unrealengine.com/t/ask-epic-verse-march-28-10-00-am-et/1765393/98?u=t.tutiya
Q:外部ファイルに書かれたパラメトリッククラスが使えないのはなぜですか?
A:これは、現在のコンパイラの実装方法に由来する物です。(コンパイル時の)型の処理は型の出現順序に大きく依存していて、パラメトリック型ではそれが最も顕著です。同じファイル内であれば、型定義の順序を入れ替えれば解決できますが、ファイルごとの(処理の)順序は指定できません。MaxVerseにおいてはpervasive leniency(「広範囲にわたる寛容さ」? コンパイラ技術用語なのかもしれないが上手く訳せず)によってこの問題を軽減します。
土屋捕捉
現在のverseでは、別ファイルで定義されたパラメトリッククラスを参照出来ないという大問題があります。おかげで公式がサンプルとして公開している汎用キュークラスがファイルを分割したままでは使用できません(なにそれ)。
でまあその理由が「コンパイル時のファイルの処理順序が不定だから(つまりはバグ)」という、モダンプログラミング言語にあるまじき回答でビックリしたという話。かつ、VMが更新されるまで解決しないっぽい……?
ちなみにこれ、C等の古い言語であれば#include文で解決し、C#などのモダンな言語ではコンパイラが対応してくれます*4。
[疑問]ライブラリの名前空間が"Temporary"って何
https://forums.unrealengine.com/t/ask-epic-verse-march-28-10-00-am-et/1765393/66?u=t.tutiya
Q:ボタンなどのUIモジュールを含む名前空間が"UnrealEngine.com/Temporary"になっているのはなぜですか? 将来的に変更する予定があるんですか?
A:"UnrealEngine.com/Temporary
"にあるAPIは将来的に移動される可能性があります(本来はVerse.org
レベルのAPIに配置すべきと考えている物がある)。移動した際、"UnrealEngine.com/Temporary
"のAPIは非推奨になる(ただし、機能は維持されます)、あるいは、"Verse.org
"において長期的サポートするAPIデザインに直接移行することになります
土屋捕捉
なんでUIのAPIが"Temporary(一時的)"モジュール配下にあるんじゃいとは長年の疑問でしたが、本当に一時的な処置だったようです(ホントかなあ?)
回答がわかりにくいですが、APIが"UnrealEngine.com/Temporary"から"Verse.org"に移動した際にインターフェイスが更新される可能性があり、その場合"UnrealEngine.com/Temporary"のAPIは互換性維持のためにインターフェイスを変えずに残すか、あるいは削除されるという意味だと思われます。
[疑問]デバイスの引数に常にagentが必要な理由
https://forums.unrealengine.com/t/ask-epic-verse-march-28-10-00-am-et/1765393/59?u=t.tutiya
Q:何故、デバイス(小道具)を制御する際、常にagent
のインスタンスが必要なのでしょうか? 内部処理上の制限でしょうか?
A:関数名やコメントからは自明ではない場合もありますが、agent
パラメータは関数内部でよく参照されています(そもそもagent
が無いと機能しない関数もあります)。
- デバイスがプレーヤー単位で状態を持つ場合、
agent
を介して個々のプレイヤーごとの状態を制御します。 - デバイスがチーム/クラス単位で状態を持つ場合、実行される関数がどのチーム/クラスに関与するかを
agent
で判断します。 - デバイスが受け取った
agent
を保持しておき、イベントが発生した際にそのagent
を渡することもあります(例:タイマーをオンにしたプレイヤーを通知する)
土屋捕捉
あるデバイスを制御する時に明示的なInstigator(扇動者。ここでは処理を実行するプレイヤーを指す)が存在しない場合があります。
例えば、「ゲーム開始後10秒したらスイッチデバイスをトグルする」という処理の場合、明示的なInstigatorは存在しません。しかし、switch_device.ToggleState()
は常に引数にagent
を取るため、以下の様に暫定的なagent
を割り当てる必要があります。
if: Agent := GetPlayspace().GetPlayers()[0] then: SwitchDevice.ToggleState(Agent) #SwitchDevice is switch_device
このそもそもInstigatorでもないagent
を取得して割り当てる必要がどこにあるのか? というのが土屋の疑問でした。
ただ、回答の2は土屋が想定していなかった物で、確かにチーム集計を行うのであれば内部的にagent
が必要になります。これは納得。
[要望]ステップ実行させてくれ
https://forums.unrealengine.com/t/ask-epic-verse-march-28-10-00-am-et/1765393/61?u=t.tutiya
Q:ステップ実行ができない環境でのデバッグ作業は非常に困難です。ステップ実行のサポート予定はありますか?
A:明確な予定はありません。ただ、私たちVerse開発者は、Verseで優れたデバッグ体験を提供する事に非常に意欲的です。
土屋捕捉
UEFN/verseは常にサーバー上で動作し、結果のみがビューとしてクライアントに表示されます*5。
この形式だとステップ実行の実装は困難でしょうからダメ元の質問でしたが、やはり予定は無いようです。
おわりに
今回はここまで。あと最低2回は続く予定です。
宣伝
Verse言語の言語仕様解説同人誌をPDFで頒布しています。よろしければどうぞ。