72.1K Views
October 30, 20
スライド概要
これは、社内勉強会で使われたスライドです。
Unreal Engineを扱う上で避けては通れない、ハード参照が連鎖する問題とその対処法を扱います。
参照連鎖に気づくのは開発が本格化してからが多いのですが、経験しないと問題の存在に気づけないという厄介さがあります。
本資料は、その予防接種としてBlueprintを扱う方に読んで頂きたい内容です。
スポットライトが当たりにくいテーマですが、トラブルを未然に防ぎプロジェクトを成功させる手助けになれば幸いです。
株式会社ヘキサドライブの資料共有用アカウントです。 公式ブログ:https://hexadrive.jp/hexablog/ note :https://note.com/hexadrive
株式会社ヘキサドライブ 堀井 陽介
目次 1 UEにおける参照とは? 2 参照が生成される理由 3 抽象に依存させよう 4 分割して疎結合とせよ 5 量産コンテンツ管理
Part 1. UEにおける 参照とは? ❖ 参照の意義を知る ❖ UE特有の参照の問題を知る
1 UEにおける参照とは? UEにおける参照について ◆ 依存しているアセットを一緒にロードしてくれ る、親切なシステム ◆ 不適切な参照はロード時間とメモリ使用量 に深刻な影響
1 UEにおける参照とは? 参照の素晴らしさ マネキンのBPをロードすると マネキンの、 ⚫ ⚫ ⚫ ⚫ ⚫ メッシュ マテリアル テクスチャ スケルトン アニメーションBP も同時にロードしてくれる
1 UEにおける参照とは? 参照の恐ろしさ 開発初期から参照関係を 適切にコントロールできていない場合、 いつのまにか数百数千のアセットが 相互依存する事態に発展しがち
1 UEにおける参照とは? 数百数千のアセットの相互依存が出来上がる原理 a) アセット同士が相互に依存 b) アセットが連鎖的に依存 >>これが繰り返され数千アセットがひとかたまりに
1 UEにおける参照とは? 相互依存が困る例 相互依存すると各BPクラスを個別にロードできなくなる >>例:緑の敵しか出ないレベルなのに、赤の敵もロードされている!
1 UEにおける参照とは? 相互依存が困る例 エディタの起動が遅くなる このアセットの適切ではない相互依存は、 ほぼBPクラス同士で起こる そして、作り方に問題を抱えている事が多い
1 UEにおける参照とは? まとめ ◆ UEの参照は、依存アセットを一緒にロードしてくれる、親切なシステム ◆ 作り方を間違えると、一緒にロードしたくないアセット同士が依存 >>不適切な参照のほとんどは、BPクラス同士で起こる ◆ 参照は連鎖する >>数千アセットが連なることも
Part 2. 参照が 生成される理由
2 参照が生成される理由 他のBPクラスの型情報が必要な操作は参照を生成する キャスト 変数型 引数や戻り値の 型 関数呼び出しの 引数
Part 3. 抽象に 依存させよう ❖ 依存を制御する術を知る
3 抽象に依存させよう 参照を切るとは? 真の問題は、参照の連鎖 参照が連鎖すると、しばしば 本質的に相互を必要としてないアセット同士が結びつく よって、不要な参照連鎖を起こさせない事が必要
3 抽象に依存させよう 単方向の依存でも悩ましい例 ◆ WBP_PlayerInfoに参照を追加すると、 WBP_PlayerInfoの総参照数が増加 ◆ BP_PlayerCharacterに参照を追加すると、 BP_PlayerCharacterとWBP_PlayerInfoの総参照数が増加 ◆ BP_Swordに参照を追加すると、 BP_SwordとBP_PlayerCharacterとWBP_PlayerInfoの総参照数が増加 11 7 3
3 抽象に依存させよう 抽象に依存し、依存を切った図 ? ?
3 抽象に依存させよう それぞれ抽象に依存すると参照アセットが波及しない 4 5 4
3 抽象に依存させよう BPクラスを抽象化する手段 C++の親クラス ◆ BPI(ブループリントインターフェース) ◆ 手ぶらの親クラス ◆ アクタータグ ◆ https://www.youtube.com/watch?v=0YMS2wnykbc Blueprints In-depth - Part 2 | Unreal Fest Europe 2019 | Unreal Engine で解説されている(日本語字幕あり)
3 抽象に依存させよう それぞれの手段の抽象を実現するクラス クライアントとなるアセットにより抽象度の高いクラスを見てもらい、自身は見せない 抽象度高 抽象度低 両者の関係 クライアントに参 照させたい型 クライアントに参 照させない型 C++親クラス C++クラス(親) BPクラス(子) 継承 BPI BPI BPクラス BPI実装 手ぶらの親クラス BPクラス(親) BPクラス(子) 継承 アクタータグ AActor BPクラス 実は継承 導入する手段
3 抽象に依存させよう C++の親クラスを抽象化に用いる AActorを直接継承せず 実装は空でもC++で親クラスを作っておく クライアントにC++型を参照させることで BP型への参照を回避できる
3 抽象に依存させよう C++クラス型の参照はタダ C++クラス型へのキャストは アセット参照が増えない BPクラスのように .uassetではないため BPクラスへのキャストの代わりに C++クラスへのキャストで 済むか検討する癖をつけると良い
3 抽象に依存させよう C++のクラスにキャストして参照を回避できそうなもの ACppCharacterBase::IsAliveはC++で実装されている点に注目 IsAliveを呼ぶために、BP_PlayerCharacterにキャストする必要は無い より抽象度の高いCppPlayerCharacterにキャストすべき
3 抽象に依存させよう BPIを抽象化に用いる 実装がBPに行われている時に有効
3 抽象に依存させよう BPIを導入するとよさそうな例 BP_GameModeを参照する いくつかのBPが左に見える これらは、BP_GameModeの 機能にアクセスしたいだけ しかし結果として BP_GameModeが参照する アセット全てを間接的に参照
3 抽象に依存させよう BPIを導入するとよさそうな例 BPI導入 BP_GameModeを参照していた BPクラス達は 代わりにBPI_GameModeを 参照するように BP_GameModeの時のように 右に続く参照の線が 無いことに注目
3 抽象に依存させよう BPI使用時の注意点 BPIも型情報を持つと参照が追加されるので注意! 引数や戻り値でBPクラス型を使うと参照が追加される。
3 抽象に依存させよう 手ぶらの親クラスを使って参照を減らす方法 末端のBPクラス(オレンジ色)にのみ アセット(リソースやBPクラス型) をもたせると参照の連鎖を切れる 中間のBPクラス(白色)はアセットを参照させない クライアント(型の利用者)は なるべく抽象度の高いクラスを見るようにする (→オレンジ色はなるべく 参照しないように! )
3 抽象に依存させよう 手ぶらの親クラスの作り方 リソースやクラス型を可能な限り持たせない 親は手ぶらにし、子にリソースを 継承の末端の子クラスにリソースやクラス型を設定
3 抽象に依存させよう アクタタグを使うことで、BPクラス型を参照しない ◆ アクタの種類判別にタグを使うとBPクラス型への参照を一切持つ必要がない ◆ タグはC++のAActorで定義されている
3 抽象に依存させよう タグを利用して参照を回避する例 敵の数を返す関数実装例 上の例はBP_EnemyCharacter BP型を参照してしまう 下の例はC++型とタグを組み合わせ、BP型を参照せず同じ事を実現
3 抽象に依存させよう まとめ ◆ クライアント(型の利用者)にはなるべく抽象度の高いクラスを参照させる ◆ アセット(グラフィックリソース等)はなるべく抽象度の低いクラスから参照させる ◆ 抽象度を高める武器 ⚫ C++の親クラス ⚫ BPI(ブループリントインターフェース) ⚫ 手ぶらの親クラス ⚫ アクタータグ
Part 4. 分割して 疎結合とせよ ❖ BPを機能で分割すると参照数削減になる理由
4 分割して疎結合とせよ こんなアセットを見たことはありませんか?
4 分割して疎結合とせよ 参照のタコ足化 沢山のアセットに参照され、沢山を参照するアセットは 多数のアセットを相互に絡める元凶に 以下を継承するBPクラスは特にタコ足化しやすい ⚫ GameMode ⚫ PlayerController ⚫ GameState ⚫ GameInstance ⚫ ACharacter
4 分割して疎結合とせよ 例:神GameMode GameModeが例えば以下を管轄していたとする ⚫ 音楽再生 ⚫ スコア管理 ⚫ 敵群衆制御 ⚫ ... すると、これら1つを使うBPは GameModeが持つ参照全てを参照 機能を分割するだけで参照数を減らせる見込みが高い
4 分割して疎結合とせよ システム分割前 19 5 18 19 6 19 4
4 分割して疎結合とせよ システム分割後 7 6 8 7 6 6 5 4 5
4 分割して疎結合とせよ Blueprint Function Libraryのタコ足化に注意 例えば、各型のオブジェクト型を返す関数ライブラリを作ってしまった場合 BP_MyFunctionLibraryは 以下を参照してしまう ● ● ● ● ● BP_PlayerCharacer BP_PlayerController BP_EnemyCharacter BP_GameMode ABP_Player
4 分割して疎結合とせよ システム分割前
4 分割して疎結合とせよ BlueprintFunctionLibraryの潜在的危険 「ライブラリ」ゆえに、様々なBPクラスから参照される BP_MyFunctionLibraryはアセット! ライブラリ自体が参照を持つのは慎重に 解決策: ● ● ● BPクラスへの参照を持たせない 関数ごとに別のBlueprintFunctionLibraryに分割する BlueprintFunctionLibraryはC++だけで実装することにする
4 分割して疎結合とせよ コンポーネントのすすめ 実装をコンポーネントに逃がすと疎結合化しやすい アクタの型を知らなくてもコンポーネントにアクセスできる
4 分割して疎結合とせよ まとめ ◆ 参照のタコ足化に警戒 神クラスは機能分割 ⚫ BlueprintFunctionLibraryがBP型を参照する事に警戒 ⚫ ◆ コンポーネントは疎結合に役立つ
Part 5. 量産コンテンツ管理 ❖ アビリティ、アニメーション、アイテム... キャラクタに参照させない作り方
5 量産コンテンツ管理 量産コンテンツの参照切り離し キャラクタが量産コンテンツを参照しないように注意 (例:アイテム、アビリティ、アニメーション) Gameplay Ability や、 Animation Montage は役に立つ 必要なエフェクト、サウンド、 アニメーションへの参照を保持してくれる キャラクタから それらを直接参照する必要がなくなる
5 量産コンテンツ管理 ABP疎結合のすすめ ABP(Animation Blueprint)は疎結合に保ちやすい ◆ ABPは「下請け」に徹すると、他のBPクラス型を参照しないようにしやすい ◆ ステートマシンよりAnimation Montage >>AMから再生するアニメーションはABPが知る必要がない >>ステートマシンに置いたアニメーションはABPが参照してしまう AnimNotifyが入り始めるとアニメーションは肥大化しがちだが、 AMにAnimNotifyを置くようにすると、ABPは身軽に保てる
5 量産コンテンツ管理 量産コンテンツを管理するデータテーブルからはソフトリファレンス
5 量産コンテンツ管理 アセットを参照するデータテーブルの扱い データテーブルが量産データ管理用なら アセットはソフトリファレンスで持つ (データアセットも同様) ポイント ◆ データテーブルを直接参照するBPクラスを最小限にしておく >>コントロールしやすい様に ◆ ソフトリファレンスは、自前のロードシステムが必要
5 量産コンテンツ管理 まとめ ◆ 量産コンテンツをキャラクタが直接持たないように ◆ Gameplay Abilityや、 Animation Montageなど 独立してアセット化できるものに量産コンテンツを結びつける ◆ データテーブルで量産コンテンツを管理するならソフトリファレンスも視野に ※ アニメーション、エフェクト、サウンド、アイテム、武具が量産コンテンツの一例
全体のまとめ
参照連鎖を断つ手法 全体のまとめ ◆ BPクラス同士は抽象に依存させると参照連鎖を切れる ⚫ ◆ C++親、BPI、手ぶらの親、アクタタグ 分割して疎結合に 多機能すぎるクラスを分割するだけで総参照数を削減を見込める ⚫ コンポーネントは疎結合化に役立つ ⚫ ◆ 量産コンテンツをキャラに持たせない Gameplay Ability、Animation Montageに持たせる ⚫ アセットを持つデータテーブルは一箇所で管理 >>ソフトリファレンス化も視野に ⚫
「 Reference ViewerとSize Mapは便利なので、 時々眺めてくれると嬉しいです。 多くの人が関心を持つことで参照連鎖は解消できます。 この資料がその一助になれば幸いです。 ご清聴ありがとうございました! 」