140.8K Views
March 18, 24
スライド概要
2024年03月16日(土)に行われた「Unreal Engine Meetup in Nagoya Season2 Vol.1」にて発表させていただいたスライドです。
イベント詳細:
https://anurabless.connpass.com/event/308577/
講演者:
加藤 優作 (株式会社ヒストリア エンジニア)
講演内容:
Unreal Engine には、「名前が似ていて混同しやすい要素」「使い分けがわかりにくい要素」「中身が違うのに見た目で区別がつかない要素」があります。
そんな紛らわしい要素をいくつか挙げて、「周辺知識」「違い・使い分け方」「押さえるべきポイント」を解説します。
株式会社ヒストリアは、Unreal Engine専門のソフトウェア開発会社です。ゲーム事業とエンタープライズ事業、2 つの軸でソフトウェアの企画、開発を行っています。また、Unreal Engine の学習を目的とした作品制作コンテスト『UE5ぷちコン』や、『出張ヒストリア! ゲーム開発勉強会』を主催、技術ブログを毎週更新など、Unreal Engine コミュニティを盛り上げる活動も行っております。
紛らわしい!何が違うの? Unreal Engine のあれこれ 2024/03/16 らりほま (加藤 優作)
自己紹介 らりほま (加藤 優作) ▶ 株式会社ヒストリア のエンジニア ▶ 開発に携わったタイトル ⚫ ライブアライブ (リードエンジニア) ▶ かつて Gray ちゃん 3D モデルを 作って公開していた気がする ⚫ KawaiiPhysics の中で生きています ▶ SNS お休み中 2
どんな講演? ▶ Unreal Engine には、 ⚫ 名前が似ていて混同しやすい要素 ⚫ 使い分けがわかりにくい要素 ⚫ 中身が違うのに見た目で区別がつかない要素 ← !? があります ▶ そんな紛らわしい要素をいくつか挙げて、 ⚫ 周辺知識 ⚫ 違い・使い分け方 ⚫ 押さえるべきポイント を解説します 3
注意事項 ① UE 5.2.1 で検証しています ② 本講演で紹介する要素はすべて Unreal C++ 絡みです ⚫ Unreal C++ を知らない人でも なるべく理解できるように 基礎的な説明を入れてはいますが、 少し難しいかもしれません ③ わかりやすさを重視した結果、 正確性に欠ける説明を あえてしている部分があります 4
目次 A) 名前が似ている要素 ① BlueprintType ② UAT UBT Blueprintable UHT ③ Unreal Build で Unity Build B) 使い分けがわかりにくい要素 ④ WITH_EDITOR ⑤ UE_SERVER WITH_EDITORONLY_DATA WITH_SERVER_CODE C) 見た目で区別がつかない要素 ⑥ 一般的な bool bitfield bool ⑦ 一般的な関数の出力ピン Set Node の出力ピン 5
① BlueprintType Blueprintable 6
① BlueprintType Blueprintable ▶ BlueprintType と Blueprintable は どちらも『クラス指定子』である ▶ 『クラス指定子』とは、 「クラスがどのように動作するかを指定するもの」である 7
① BlueprintType Blueprintable ▶ Blueprint のアセットを作るときの手順 ⚫ 親となるクラスを選んで ⚫ 名前を付けて ⚫ 必要であればオプションを変更する 8
① BlueprintType Blueprintable ▶ C++ でクラスを作るときも同じ ⚫ 親となるクラスを選んで ⚫ 名前を付けて ⚫ 必要であればオプションを変更する ▶ クラス指定子は、この『オプション』に該当するもの 9
① BlueprintType Blueprintable ▶ それぞれの効果は? ▶ 公式ドキュメントによると…… ⚫ BlueprintType • ブループリントの変数に使用できるタイプとしてクラスを公開します ⚫ Blueprintable • ブループリントの作成に使用できる基本クラスとしてこのクラスを公開します クラス指定子 | Unreal Engine 5.3 ドキュメント https://docs.unrealengine.com/5.3/ja/class-specifiers/ 10
① BlueprintType Blueprintable ▶ BlueprintType についての検証 ⚫ 以下の 2 パターンの状況で、 ATestActor クラスの 変数が作れるかどうかを確認する A) ATestActor に BlueprintType を指定したとき B) ATestActor に NotBlueprintType を指定したとき (NotBlueprintType = BlueprintType と逆の効果を持つ指定子) 11
① BlueprintType Blueprintable ▶ BlueprintType についての検証結果 A) BlueprintType を指定したとき ⇒ ATestActor の変数が作成できる B) NotBlueprintType を指定したとき ⇒ ATestActor の変数が作成できない (変数の型選択時に候補に出ない) 12
① BlueprintType Blueprintable ▶ Blueprintable についての検証 ⚫ 以下の 2 パターンの状況で、 ATestActor クラスを親とする Blueprint クラスが作れるかどうかを確認する A) ATestActor に Blueprintable を指定したとき B) ATestActor に NotBlueprintable を指定したとき (NotBlueprintable = Blueprintable と逆の効果を持つ指定子) 13
① BlueprintType Blueprintable ▶ Blueprintable についての検証結果 A) Blueprintable を指定したとき ⇒ ATestActor を親とする Blueprint クラスが作成できる B) NotBlueprintable を指定したとき ⇒ ATestActor を親とする Blueprint クラスが作成できない (クラス作成時に候補に出ない) 14
① BlueprintType Blueprintable ▶ 覚え方 ⚫ AActor 継承クラスはデフォルトで BlueprintType かつ Blueprintable • よって、通常はわざわざこれらを指定する必要はない ⚫ 構造体 (USTRUCT) や列挙型 (UENUM) は デフォルトで NotBlueprintType • よって、Blueprint 上で利用するために BlueprintType を指定することが多い ⇒ 構造体・列挙型でよく使う BlueprintType だけ覚えておけば OK ※ USTRUCT や UENUM に対する Blueprintable 指定は無効だが、 ビルドは通ってしまうので BlueprintType と混同しないように注意 15
① BlueprintType Blueprintable ▶ まとめ ⚫ BlueprintType • Blueprint の『変数』作成の際に 『型の候補』として選択可能になるクラス指定子 ⚫ Blueprintable • Blueprint の『クラス』作成の際に 『親の候補』として選択可能になるクラス指定子 ⚫ 混同してもエラーが発生しないことがあり、 その場合は間違いに気づきにくいため注意 16
② UAT UBT UHT 17
② UAT UBT UHT ▶ 何の略? ⚫ UAT : UnrealAutomationTool ⚫ UBT : UnrealBuildTool ⚫ UHT : UnrealHeaderTool ▶ これらはすべて Unreal Editor から独立したツールの名前 18
② UAT UBT UHT ▶ どんなツール? ⚫ UnrealAutomationTool • ゲームのビルドやテストなどの 様々なプロセスを自動化するためのツール ⚫ UnrealBuildTool • ビルドプロセスを管理・実行するツール ⚫ UnrealHeaderTool • Unreal Engine 固有のマクロを含む ヘッダファイルを解析して メタデータ・コードを生成するツール ⇒ いずれもビルド時に利用される 19
② UAT UBT UHT ② Unreal Engine 独自の処理 ▶ ビルドフローでの使われ方 ③ ① UAT もしくは Unreal Editor にて ビルド関連コマンドが実行されると、 UBT が呼び出される ② UBT はビルドに必要な各種処理を実行する [UnrealHeaderTool] ヘッダの解析 コードの生成 [プリプロセッサ] [UnrealBuildTool] ビルドの管理・実行 プリプロセス (プリプロセッサディレクティブの処理) ③ 一般的な C++ のビルド処理の前に Unreal Engine 独自の処理が実行されるが、 そのうちのひとつが UHT である [コンパイラ] コンパイル [リンカ] リンク ① [UnrealAutomationTool] プロセスの自動化 [Unreal Editor] 実行可能 ファイル 20
② UAT UBT UHT ▶ UnrealHeaderTool の処理内容 ❝ Unreal Engine 固有のマクロを含む ヘッダファイルを解析して メタデータ・コードを生成するツール ❞ ① 『Unreal Engine 固有のマクロ』とは? ② コードはどこに生成される? ③ UHT があると何が嬉しいのか? 21
② UAT UBT UHT ① 『Unreal Engine 固有のマクロ』とは? ⚫ 具体例 • UCLASS / USTRUCT / UENUM • UFUNCTION / UPROPERTY • GENERATED_BODY • など ⚫ 先ほどの『BlueprintType 一部登場していた Blueprintable』の説明内にも 22
② UAT UBT UHT ② コードはどこに生成される? ⚫ Intermediate ディレクトリ以下に書き出される • 例 : “Test_0502C” プロジェクトにおける TestActor.h/.cpp に対応する生成コード • ディレクトリ : • Test_0502C\Intermediate\Build\Win64\UnrealEditor\Inc\Test_0502C\UHT\ • ファイル名 : • TestActor.generated.h • TestActor.gen.cpp ⚫ ヘッダファイルに #include “~.generated.h” と書くルールがあるのは、 UHT が生成したコードを読み込むため 23
② UAT UBT UHT ② コードはどこに生成される? ⚫ 生成されたコードの中身 (.generated.h) 24
② UAT UBT UHT ③ UHT があると何が嬉しいのか? ⚫ 少しのコード記述で、開発効率の向上や柔軟な制御が実現できる • • 例 : UPROPERTY マクロ • 変数の初期値を Editor 上で変更できる • 変数の Get / Set Node が Blueprint Editor 上で利用できる UHT が生成するメタデータやコードがこれらを実現 25
② UAT UBT UHT Unreal Engine 独自の処理 ▶ まとめ ⚫ UnrealAutomationTool (UAT) • ゲームのビルドやテストなどの 様々なプロセスを自動化するためのツール • ビルド関連のコマンドを実行するときに UBT を呼び出す ⚫ UnrealBuildTool (UBT) [UnrealHeaderTool] ヘッダの解析 コードの生成 [プリプロセッサ] [UnrealBuildTool] ビルドの管理・実行 プリプロセス (プリプロセッサディレクティブの処理) • ビルドプロセスを管理・実行するツール • ビルドに必要な処理の一部として UHT を呼び出す [コンパイラ] コンパイル ⚫ UnrealHeaderTool (UHT) • Unreal Engine 固有のマクロを含む ヘッダファイルを解析して コードを生成するツール • 開発効率の向上や柔軟な制御を実現する [リンカ] リンク [UnrealAutomationTool] プロセスの自動化 [Unreal Editor] 実行可能 ファイル 26
③ Unreal Build で Unity Build 27
③ Unreal Build で Unity Build ▶ Q. Unreal Engine Meetup で 別のゲームエンジンの話とな??? ▶ A. 落ち着いてください 28
③ Unreal Build で Unity Build Unreal Engine 独自の処理 [UnrealHeaderTool] ▶ Unity Build とは…… ⚫ Unreal Engine の一機能 [Unity Build] ソースコードの結合 • ゲームエンジンの Unity とは無関係 ⚫ ビルド時にモジュール内の 複数のソースコードを結合する機能 [プリプロセッサ] [UnrealBuildTool] ビルドの管理・実行 プリプロセス (プリプロセッサディレクティブの処理) [コンパイラ] コンパイル [リンカ] リンク [UnrealAutomationTool] プロセスの自動化 [Unreal Editor] 実行可能 ファイル 29
③ Unreal Build で Unity Build ▶ 結合されたコードはどこに書き出される? ⚫ Intermediate ディレクトリ以下に書き出される (UHT と同じ) • 例 : Engine の UnrealEditor における Core モジュールの結合コード • ディレクトリ : • Engine\Intermediate\Build\Win64\x64 \UnrealEditor\Development\Core • ファイル名 : • Module.Core.1_of_17.cpp • Module.Core.2_of_17.cpp • … ※ UE 5.3 からは Module.Core.1.cpp のようにファイル名が簡素になったらしい……? 30
③ Unreal Build で Unity Build ▶ 結合ではなく分割されているように見えるけど……? ⚫ 結合した上で長すぎる場合に分割される • Module.Core.1_of_17.cpp, Module.Core.2_of_17.cpp, … のようにファイルが 生成されている場合、 Core モジュールのコード全体が 結合された上で 17 分割されている 31
③ Unreal Build で Unity Build ▶ コードの中身は? ⚫ #include だらけ • #include とは? • プリプロセッサで処理される ディレクティブ (指令) のひとつ • 指定したファイルの中身が その場所に展開 (コピペ) される ⚫ 『結合されたコード』 というよりも 『結合を指示するコード』 という方が正しい 32
③ Unreal Build で Unity Build ▶ 処理の流れ Unreal Engine 独自の処理 ① Unity Build が 『結合を指示するコード』を生成する ① ② プリプロセッサが 『結合を指示するコード』の中の #include ディレクティブを解釈し、 『結合されたコード』を生成する ② ③ コンパイラが 『結合されたコード』をコンパイルする [Unity Build] ソースコードの結合 [プリプロセッサ] プリプロセス (プリプロセッサディレクティブの処理) ③ [コンパイラ] コンパイル [リンカ] リンク 実行可能 ファイル 33
③ Unreal Build で Unity Build ▶ 結合によるメリット ⚫ ビルド時間短縮のため • 対象ファイル数に比例して増加する 処理のオーバーヘッドが減少する • 共通で #include している ヘッダファイルの読み込み回数が減少する ⚫ 最適化のため • 通常のコンパイル単位では実行されない 最適化処理が適用される可能性がある 34
③ Unreal Build で Unity Build ▶ 結合によるデメリット ⚫ 名前の衝突が発生することがある • ファイルスコープの static 変数など • コンパイルエラー発生の原因になる • 意図しない挙動が引き起こされる可能性もある ⚫ ヘッダの #include 漏れが隠蔽されることがある • 必要なヘッダの #include を行っていない (本来はコンパイルエラーになるべき) コードが 結合対象のコードの #include に救われて コンパイルを通過してしまうことがある • この場合、結合単位が変化したタイミングで コンパイルエラーが発生するようになる 35
③ Unreal Build で Unity Build ▶ Unity Build を無効化する方法 ⚫ モジュールの Target.cs に以下を記述する bUseUnityBuild = false; ⚫ BuildConfiguration.xml の編集でも無効化できる • Unreal Engine のビルド コンフィギュレーション | Unreal Engine 5.3 ドキュメント https://docs.unrealengine.com/5.3/ja/build-configuration-for-unreal-engine/ 36
③ Unreal Build で Unity Build ▶ まとめ ⚫ Unity Build とは、 ビルド時にモジュール内の 複数のソースコードを結合する Unreal Engine の機能である ⚫ ゲームエンジンの Unity とは無関係 ⚫ 主にビルド時間短縮のために実行される ⚫ 結合によってエラーや意図せぬ挙動が 引き起こされることがあるので注意 ⚫ モジュール単位で無効化することができる 37
③ Unreal Build で Unity Build ▶ ちなみに ⚫ 弊社ブログでも解説されています • [UE4] Unityビルドシステム|株式会社ヒストリア https://historia.co.jp/archives/4393/ 38
④ WITH_EDITOR WITH_EDITORONLY_DATA 39
④ WITH_EDITOR WITH_EDITORONLY_DATA ▶ WITH_EDITOR と WITH_EDITORONLY_DATA は どちらも『マクロ』である ▶ マクロは #if #endif などと組み合わせることで 「特定の条件下でのみコンパイル対象となるコード」 を指定することができる 40
④ WITH_EDITOR WITH_EDITORONLY_DATA ▶ 例 : マクロ『HOGEHOGE』を 1 で定義した場合 ⚫ #if HOGEHOGE ~ #endif までのコードは有効 (コンパイルされる) 41
④ WITH_EDITOR WITH_EDITORONLY_DATA ▶ 例 : マクロ『HOGEHOGE』を 0 で定義した場合 ⚫ #if HOGEHOGE ~ #endif までのコードは無効 (コンパイルされない / なかったことにされる) 42
④ WITH_EDITOR WITH_EDITORONLY_DATA ▶ Unreal Engine では、ビルド時に UBT が ビルド設定に応じた適切なマクロを定義してくれる ⚫ 例 : PLATFORM_WINDOWS • ターゲットプラットフォームが Windows のときに 1 で定義 ⚫ 例 : UE_BUILD_DEVELOPMENT • ビルドコンフィギュレーションが Development のときに 1 で定義 ▶ WITH_EDITOR と WITH_EDITORONLY_DATA は このようなマクロの一例 43
④ WITH_EDITOR WITH_EDITORONLY_DATA ▶ WITH_EDITOR と WITH_EDITORONLY_DATA について 直接的に解説しているドキュメントはなさそう ▶ エンジンコードや検証からわかった事実を解説 ① それぞれの値と条件 ② UPROPERTY を #if WITH_EDITOR ~ #endif で囲むと UHT がエラーを吐く件について ③ エンジンコード内での使い分け 44
④ WITH_EDITOR WITH_EDITORONLY_DATA ① それぞれの値と条件 WITH_EDITOR = 0 WITH_EDITOR = 1 WITH_EDITORONLY_DATA = 0 Game なし WITH_EDITORONLY_DATA = 1 Editor 用プロパティを 一部利用するツール (UnrealHeaderTool / Datasmith など) Unreal Editor ⚫ 「Editor ではないが、一部の Editor 用プロパティを使いたい」 というツールがいくつか存在しており、 それらのビルド時のみ、両者は異なる値を取る • WITH_EDITOR = 0 • WITH_EDITORONLY_DATA = 1 45
④ WITH_EDITOR WITH_EDITORONLY_DATA ② UPROPERTY を #if WITH_EDITOR ~ #endif で囲むと UHT がエラーを吐く件について ⚫ error : UProperties should not be wrapped by WITH_EDITOR, use WITH_EDITORONLY_DATA instead. ⚫ 『Editor 用プロパティを一部利用するツール』が UPROPERTY 指定によって生成されるメタデータを 利用する可能性があるため、隠蔽を防ぐために 一律でエラー扱いにしているものと推測 46
④ WITH_EDITOR WITH_EDITORONLY_DATA ③ エンジンコード内での使い分け ⚫ 以下のように使い分けられているケースが多い • UPROPERTY 指定されたメンバ変数の宣言は #if WITH_EDITORONLY_DATA ~ #endif で囲む • 上記のようなメンバ変数を利用するコードは同じく #if WITH_EDITORONLY_DATA ~ #endif で囲む • それ以外の場合では #if WITH_EDITOR ~ #endif で囲む ⚫ 「WITH_EDITOR はコード用」 「WITH_EDITORONLY_DATA はデータ用」 と解説されていることがあるが、一概にそうとも限らない 47
④ WITH_EDITOR WITH_EDITORONLY_DATA ▶ まとめ ⚫ ⚫ ⚫ ⚫ WITH_EDITOR = 0 WITH_EDITOR = 1 WITH_EDITORONLY_DATA = 0 Game なし WITH_EDITORONLY_DATA = 1 Editor 用プロパティを 一部利用するツール (UnrealHeaderTool / Datasmith など) Unreal Editor WITH_EDITOR と WITH_EDITORONLY_DATA は基本的には 「Editor ビルドかどうか」で値が設定されるマクロである 一部ツールのビルド時に、両者は異なる値を取ることがある UPROPERTY を #if WITH_EDITOR ~ #endif で囲むことは 禁止されているので、代わりに #if WITH_EDITORONLY_DATA ~ #endif で囲む 使い分け方はエンジンコードを参考にするとよい 48
④ WITH_EDITOR WITH_EDITORONLY_DATA ▶ 参考情報 ⚫ エンジンコード (括弧内は行番号) • Engine\Source\Programs\UnrealBuildTool \Configuration\UEBuildTarget.cs (4628) • Engine\Source\Programs\UnrealHeaderTool \UnrealHeaderTool.Target.cs (20) • Engine\Source\Runtime\Core \Public\Misc\CoreMiscDefines.h (23) 49
④ WITH_EDITOR WITH_EDITORONLY_DATA ▶ 参考情報 ⚫ フォーラム記事 • WITH_EDITOR vs WITH_EDITORONLY_DATA - need clarification Programming & Scripting / C++ - Epic Developer Community Forums https://forums.unrealengine.com/t/with-editor-vs-with-editoronly-dataneed-clarification/103182 ⚫ 弊社ブログ (WITH_EDITORONLY_DATA の活用事例) • [UE4] 独自に用意したデータアセットをより便利に活用する方法|株式会社ヒス トリア https://historia.co.jp/archives/13665/ 50
④ WITH_EDITOR WITH_EDITORONLY_DATA ▶ 補足 (やぶ蛇) ⚫ UE_EDITOR というマクロも存在する • UE_EDITOR • ターゲットタイプが Editor の場合に 1 で定義される • 「ターゲットタイプが Editor かどうか」という観点で使用する • WITH_EDITOR • ReadOnlyTargetRules::bCompileAgainstEditor が true の場合、 ターゲットタイプが Program の場合でも 1 で定義される • 「Editor 的な振る舞いが必要かどうか」という観点で使用する ※ 『ターゲットタイプ』については次の項で解説します 51
⑤ UE_SERVER WITH_SERVER_CODE 52
⑤ UE_SERVER WITH_SERVER_CODE ※ WITH_EDITOR WITH_EDITORONLY_DATA と 似たような話なので手短に済ませます ▶ UE_SERVER と WITH_SERVER_CODE も UBT で定義されるマクロである ▶ どちらも、ビルド設定の 『ターゲットタイプ』に応じて 値が定義される 53
⑤ UE_SERVER WITH_SERVER_CODE ▶ 『ターゲットタイプ』とは? ⚫ ビルドプロセスが生成する アプリケーションの種類を指定するもの ⚫ 種類 • Game : Server コードと Client コードを両方含むスタンドアローンゲーム • Client : Game から Server コードを除いたもの • Server : Game から Client コードを除いたもの • Editor : Unreal Editor の拡張 • Program : UHT のようなツール 54
⑤ UE_SERVER WITH_SERVER_CODE ▶ いきなり結論 & まとめ ⚫ UE_SERVER • 『ターゲットタイプ』が Server のときに 1 で定義される ⚫ WITH_SERVER_CODE • Server 用コードが含まれている場合に 1 で定義される • Game も Server 用コードを含んでいるので 1 UE_SERVER WITH_SERVER_CODE Game 0 1 Client 0 0 Server 1 1 55
⑤ UE_SERVER WITH_SERVER_CODE ▶ 補足 (やぶ蛇) ⚫ UE_GAME と UE_CLIENT というマクロもある ⚫ それぞれの定義は? • UE_XXX マクロは「ターゲットタイプが XXX のときに 1 で定義される」 みたいだから、以下の表のようになる……? UE_GAME UE_CLIENT UE_SERVER WITH_SERVER_CODE Game 1 0 0 1 Client 0 1 0 0 Server 0 0 1 1 56
⑤ UE_SERVER WITH_SERVER_CODE ▶ 補足 (やぶ蛇) ⚫ 違います! • ターゲットタイプが Client のときも UE_GAME は 1 で定義されるので注意 UE_GAME UE_CLIENT UE_SERVER WITH_SERVER_CODE Game 1 0 0 1 Client 1 1 0 0 Server 0 0 1 1 57
⑤ UE_SERVER WITH_SERVER_CODE ▶ 補足 (やぶ蛇) ⚫ Engine\Source\Programs\UnrealBuildTool\Configuration\UEBu ildTarget.cs より 58
⑥ 一般的な bool bitfield bool 59
⑥ 一般的な bool bitfield bool ▶ 「Unreal Editor 上で bool として扱われる変数」 の宣言方法は 2 通りある ① bool 型で宣言する方法 ② 符号なし整数型を基にした 1 bit の bitfield で宣言する方法 • 本講演では、この方法で宣言した変数を 『bitfield bool』と呼ぶことにします 60
⑥ 一般的な bool bitfield bool ① bool 型で宣言する方法 ⚫ 一般的な宣言方法 ⚫ bool 型変数のサイズは 1 Byte になる • 厳密に言うと処理系依存だが、 実用上 1 Byte と考えて差し支えない • bool は true と false の 2 値を表す型だが、 コンピュータのメモリシステムは バイト単位でのアクセスが基本となるため、 通常 1 bit ではなく 1 Byte となる 61
⑥ 一般的な bool bitfield bool ② 符号なし整数型を基にした 1 bit の bitfield で宣言する方法 (bitfield bool) ⚫ エンジンコードでもしばしば見られる宣言方法 ⚫ 変数名:1 という形で宣言する ⚫ 型には uint32 または uint8 が使われる ⚫ この変数のサイズは 1 bit になる • (と言い切るのは厳密には誤りなのですが、 ここではこの前提で話を進めます) 62
⑥ 一般的な bool bitfield bool ▶ 比較 ⚫ Unreal Editor 上では どちらの方法で宣言しても bool 変数として扱われ、見分けがつかない 63
⑥ 一般的な bool bitfield bool ▶ bitfield bool の特徴 ⚫ メリット • メモリ使用量を削減できる • 通常の bool では 1 Byte (8 bit) 消費するが、 bitfield bool では 1 bit の消費で済む ⚫ デメリット • アクセス速度が遅くなる可能性がある • アドレッサブルでない • メモリはバイト単位でアドレスが割り振られているため、 1 bit のみを占める bitfield bool の変数には 独立したアドレスを持たせることができず、 結果として直接アクセスできない 64
⑥ 一般的な bool bitfield bool ▶ bitfield bool はアドレッサブルでないので…… ⚫ アドレスを取得するような処理を C++ で書くとコンパイルエラーになる 65
⑥ 一般的な bool bitfield bool ▶ ここで問題が…… ⚫ Unreal Editor 上では bitfield bool は通常の bool と同じ扱いになるため、 実はアドレスを扱う処理を書けてしまう ⚫ このような処理を実行すると、 対象の bitfield bool 以外の変数の内容が 破壊されてしまう可能性がある 66
⑥ 一般的な bool bitfield bool ▶ 検証 ⚫ 以下の手順を一般的な bool と bitfield bool で試す ① テスト用のクラスに 8 個の bool 変数を 初期値 true で連続で宣言する ② 「bool の参照を取得してその値を false にする関数」 を Blueprint で作成する ③ 一番最初に宣言した bool 変数を上記関数に与えて実行し、 すべての bool 変数の値を確認する 67
⑥ 一般的な bool bitfield bool ▶ 検証準備 ⚫ 「bool の参照を取得してその値を false にする関数」を作る • 入力ピン bBool を Pass-by-Reference (参照渡し) にする 68
⑥ 一般的な bool bitfield bool ▶ 検証準備 ⚫ bool 値確認用の処理を作る 69
⑥ 一般的な bool bitfield bool ▶ 検証 : 普通の bool の場合 70
⑥ 一般的な bool bitfield bool ▶ 検証結果 : 普通の bool の場合 ⚫ 関数 SetBoolToFalse の対象である 変数 bBool1 だけが false になった ⚫ 他の変数の値は初期値 true のまま ⚫ 期待通りの挙動 71
⑥ 一般的な bool bitfield bool ▶ 検証 : bitfield bool の場合 ⚫ あくまで実態は uint32 なので 1 で初期化 72
⑥ 一般的な bool bitfield bool ▶ 検証結果 : bitfield bool の場合 ⚫ 関数 SetBoolToFalse の対象である 変数 bBool1 が false になったのは同じ ⚫ しかし、他の変数の値まで false になってしまった ⚫ 意図しない挙動 73
⑥ 一般的な bool bitfield bool ▶ 何故このような現象が起こるのか ⚫ 一般的な bool の場合 SetBoolToFalse() 対象の bool のアドレスは 0x1000 だから、 ここに 00000000 (false) を入れよう 0x1000 0x1001 0x1002 0x1003 0x1004 0x1005 0x1006 0x1007 bBool1 bBool2 bBool3 bBool4 bBool5 bBool6 bBool7 bBool8 00000001 (true) 00000001 (true) 00000001 (true) 00000001 (true) 00000001 (true) 00000001 (true) 00000001 (true) 00000001 (true) 74
⑥ 一般的な bool bitfield bool ▶ 何故このような現象が起こるのか ⚫ 一般的な bool の場合 SetBoolToFalse() 対象の bool のアドレスは 0x1000 だから、 ここに 00000000 (false) を入れよう 0x1000 0x1001 0x1002 0x1003 0x1004 0x1005 0x1006 0x1007 bBool1 bBool2 bBool3 bBool4 bBool5 bBool6 bBool7 bBool8 00000001 (true) 00000001 (true) 00000001 (true) 00000001 (true) 00000001 (true) 00000001 (true) 00000001 00000000 00000001 (false) (true) (true) 75
⑥ 一般的な bool bitfield bool ▶ 何故このような現象が起こるのか ⚫ bitfield bool の場合 SetBoolToFalse() bBool1 を表現しているのは 0x1000 の最下位 bit だけで、 他の bit には他の変数の値が 入っていることに気づかない 対象の bool のアドレスは 0x1000 だから、 ここに 00000000 (false) を入れよう 0x1000 0x1000 bBool8-1 bBool8 bBool7 bBool6 bBool5 bBool4 bBool3 bBool2 bBool1 11111111 (全 true) 1 (true) 1 (true) 1 (true) 1 (true) 1 (true) 1 (true) 1 (true) 1 (true) 76
⑥ 一般的な bool bitfield bool ▶ 何故このような現象が起こるのか ⚫ bitfield bool の場合 SetBoolToFalse() 対象の bool のアドレスは 0x1000 だから、 ここに 00000000 (false) を入れよう 0x1000 0x1000 bBool8-1 bBool8 bBool7 bBool6 bBool5 bBool4 bBool3 bBool2 bBool1 11111111 00000000 (全 (全 false) true) 0 (false) (false) 0 (false) (false) 0 (false) (false) 0 (false) (false) 0 (false) (false) 0 (false) (false) 0 (false) (false) 0 (false) (false) 巻き添え 77
⑥ 一般的な bool bitfield bool ▶ まとめ ⚫ 「Unreal Editor 上で bool として扱われる変数」 の宣言方法は 2 通りある ① bool 型で宣言する方法 ② 符号なし整数型を基にした 1 bit の bitfield で宣言する方法 (bitfield bool) ⚫ どちらの方法で宣言しても bool 変数として同じように扱われるため、 Unreal Editor 上では見分けがつかない ⚫ bitfield bool にはメモリ使用量を削減するメリットがあるが、 Blueprint 上で bool の参照を取る関数に入力として与えた場合に 他の変数の値を破壊する恐れがある 78
⑥ 一般的な bool bitfield bool ▶ 対策どうする? ⚫ bitfield bool を使わない • エンジンで定義済の bitfield bool についてはどうしようもない ⚫ 正真正銘 bool で宣言されている変数のみ bool 参照入力に入れることにする • いちいち調べる必要がある ⚫ bool 参照入力を取る関数を作らない・使わない • 結果の bool を出力するようにして 必要であれば関数の外側で変数に代入させる ⚫ 本当は Blueprint 側でコンパイルエラーにしてほしい…… 79
⑦ 一般的な関数の出力ピン Set Node の出力ピン 80
⑦ 一般的な関数の出力ピン Set Node の出力ピン ▶ 問題です ⚫ ランダムな整数を出力する non-pure 関数『Rand』を作ります ⚫ Rand を BeginPlay で 1 回実行します 81
⑦ 一般的な関数の出力ピン Set Node の出力ピン ▶ 問題です ⚫ その後、Tick で Rand の出力ピンの値を PrintString し続けます (Rand は実行はしません) ⚫ どのような値が表示されるでしょうか? • ずっと同じ値? • 毎回異なる値? 82
⑦ 一般的な関数の出力ピン Set Node の出力ピン ▶ 正解は…… ⚫ ずっと同じ値が表示され続けます • 厳密に言うと、 最後に関数を実行したとき (BeginPlay のとき) の結果の値が ずっと表示され続けます 83
⑦ 一般的な関数の出力ピン Set Node の出力ピン ▶ 何故そうなるのか? ⚫ 一般的な non-pure 関数の Node の出力ピンは 最後に実行されたときの値をキャッシュしている • 『一般的な non-pure 関数の Node』 とは以下を指す • Blueprint で作成されたもの • UFUNCTION 指定で公開されたもの • Node 単位でのキャッシュであり、 同じ名前の Node だからといって 値の共有はされない 84
⑦ 一般的な関数の出力ピン Set Node の出力ピン ▶ 未実行の Node の出力ピンの扱い ⚫ その型のデフォルト値を返却する • bool なら false • int32 なら 0 • など 85
⑦ 一般的な関数の出力ピン Set Node の出力ピン ▶ さらに問題です ⚫ 以下のような Node が組まれている場合、 Tick 時にどのような値が表示されるでしょうか? • ずっと同じ値? • 毎回異なる値? 86
⑦ 一般的な関数の出力ピン Set Node の出力ピン ▶ 正解は…… ⚫ 毎回異なる値が表示されます 87
⑦ 一般的な関数の出力ピン Set Node の出力ピン ▶ Set Node についてさらに検証すると…… ⚫ どうやら Set Node の出力ピンは 最後に実行されたときの キャッシュ値ではなく、 常に最新の変数の値を 返しているらしい ⚫ 出力ピンのツールチップに 「個別の Get Node 代わりに 使える」と書いてある 88
⑦ 一般的な関数の出力ピン Set Node の出力ピン ▶ 何故そうなるのか? ⚫ Set Node は、一般的な関数の Node とは異なり、 UK2Node クラスを継承して作成されている • Set Node のクラス名は UK2Node_VariableSet • “K2” はおそらく “Kismet 2” の略 • Unreal Engine 3 には UnrealKismet という ビジュアルスクリプティングツールが存在していた • Kismet 2 = UnrealKismet のバージョンアップ版 = Blueprint ⚫ UK2Node クラスを継承して Node を作成すると より細やかな処理の制御が可能となる 89
⑦ 一般的な関数の出力ピン Set Node の出力ピン ▶ 何故そうなるのか? ⚫ Set Node は、出力ピンにワイヤーが接続されている場合に Get Node を生成して接続する処理を実行している • UK2Node_VariableSet::ExpandNode() の定義参照 90
⑦ 一般的な関数の出力ピン Set Node の出力ピン ▶ 何故そうなるのか? ⚫ つまり、以下の 2 つは同等の処理である = 91
⑦ 一般的な関数の出力ピン Set Node の出力ピン ▶ Set Node の実装詳細を知っていれば、 += の処理はこのように書けることがわかる ⚫ コンパクトに書けるが パッと見で処理内容がわかりにくいので、 仕事ではあまり使わない方がよいかも…… 92
⑦ 一般的な関数の出力ピン Set Node の出力ピン ▶ まとめ ⚫ 一般的な non-pure 関数の Node の出力ピンは 最後に実行されたときの値をキャッシュしている • 『一般的な non-pure 関数の Node』 とは以下を指す • Blueprint で作成されたもの • UFUNCTION 指定で公開されたもの ⚫ Node は UK2Node を継承して作成することもでき、 その場合は出力ピンの挙動をカスタマイズできるため、 値のキャッシュが行われるとは限らない • 例えば、Set Node の出力ピンは Get Node と同等になる 93
目次 (振り返り) A) 名前が似ている要素 ① BlueprintType ② UAT UBT Blueprintable UHT ③ Unreal Build で Unity Build B) 使い分けがわかりにくい要素 ④ WITH_EDITOR ⑤ UE_SERVER WITH_EDITORONLY_DATA WITH_SERVER_CODE C) 見た目で区別がつかない要素 ⑥ 一般的な bool bitfield bool ⑦ 一般的な関数の出力ピン Set Node の出力ピン 94
紛らわしい要素に出会ったら ▶ 調査 ⚫ “XXX vs YYY” で検索 ⚫ エンジンソースコードを読む ⚫ 「恐らくこうだろう」という仮説を立てる ▶ 検証 ⚫ 検索で出てきた情報を鵜呑みにせず検証する ⚫ 立てた仮説が正しいか確認する ⚫ 検証用プロジェクトを用意しておくことを推奨 ▶ メモ ⚫ 忘れたとき・人に説明するときのためにメモしておく ⚫ メモはすぐに引っ張り出せるようにする • 個人的には Notion がオススメ 95
告知 ▶ 今年もやります! ⚫ https://gamemakers.jp/2024_gms_news/ 96
ご清聴ありがとうございました! 97