66.1K Views
May 12, 22
スライド概要
UE4プログラマー向け勉強会 in 大阪で喋った時に使用したスライドです。 https://connpass.com/event/76815/
UE4におけるキャラクタークラス設計 Indie-us Games 代表&クリエイター alwei
自己紹介 株式会社Indie-us Games代表取締役 名前 : alwei Twitter : @aizen76 Unreal Engine 4 専門会社 関西でUE4をやりたい人を絶賛募集中!
今回プログラマー向けというわけで…
いつもより少しだけ難しめの話をします٩( ‘ω’ )و
基本的なオブジェクト指向の理解が あると恐らくスムーズに理解できるハズ
テーマ『キャラクタークラス設計』
今作ってるゲーム 趣味で個人開発しているTPSのゲームを参考に。 個人で開発中のゲーム 『Blue Gunner』 今年中に完成… 出来たらいいな……
一般的なオブジェクト指向の3原則 ・継承 ・多態性 ・カプセル化
一般的なオブジェクト指向の3原則 ・継承 ・多態性 ・カプセル化 ← 本質的に重要なのはここ
一体なぜ…? 継承と多態性は、UE4を使っていると 何も考えなくともほぼ確実に利用しています。 ブループリントのアクターは常に継承。 関数はあらゆるところでオーバーライドされています。
カプセル化って? 意外なくらい理解していない人が多い。 むしろこれこそがオブジェクト指向の本質。 以下のURLの説明はとても参考になります。 オブジェクト指向と10年戦ってわかったこと https://qiita.com/tutinoco/items/6952b01e5fc38914ec4e
現実世界でのカプセル化 私達が普段使っているUSB(Universal Serial Bus)は 最もわかりやすいカプセル化の例です。 様々な機器に挿すだけで自動的に機能が呼び出されます。 これは『挿す』という関数をUSBというインターフェースを 使って呼び出したということになります。
相手のことはよく知らない カプセル化は必要以上に相手のことを知る必要はありません。 制限されたインターフェースを使うことで、 お互いのことを知らないままでも機能を使うことが出来ます。 これがUE4においても非常に重要になってきます。
なぜ重要? UE4で作ったプロジェクトは常に大規模になりやすい。 カプセル化を常に意識して作らなければ、 ブループリントやC++でも参照関係が複雑となり、 読み込みが遅くなったり、不安定なバグを生み出す原因に。 特に商業ゲームではこれを意識しないと開発後半で泣く。
UE4におけるカプセル化
インターフェースシステム UE4ではインターフェースを使った、 オブジェクト間の依存を持たせない仕組みが存在。 『ブループリント インターフェース』 https://docs.unrealengine.com/latest/JPN/Engine/Blueprints/ UserGuide/Types/Interface/index.html 『インターフェース(C++)』 https://docs.unrealengine.com/latest/JPN/Programming/Unre alArchitecture/Reference/Interfaces/index.html
インターフェースシステム インターフェースを使うとお互いの情報を完全に隠蔽し、 依存関係を持たないまま、相手の機能を呼び出せます。 USBインターフェース パソコンはマウスの事を知らないが、 マウスの機能を使う事が出来る。
UE4で実際に使うと… キャラクターブループリント側から アニメーションブループリントヘインターフェースで呼び出し BPインターフェース キャラクターブループリント アニメーションブループリント
外部とのやりとりの隠蔽 外とのやりとりは基本的にインターフェースを経由 Weapon BP Inventory BP Enemy Character BP Anim BP
依存関係のチェック リファレンスビューワーを使うと依存関係がわかります。 アセットを右クリックして開く。
依存関係には常に注意 何が問題になる? ・アセット依存による読み込み時間の増大 ・依存BP(C++)の増大によるコンパイル時間の増大 ・不要な依存や一部破損によるクラッシュ率の増大 ・不要アセットがパッケージされてデータ容量の増大 ・ソースコントロール上で不要なチェックアウトの発生 etc..
大規模な開発ほど注意が必要 よくやっちゃうけどCastとかもあまりよくない…
継承 vs 包含
オブジェクト指向で良くある論議 継承(is-a)すべき?包含(has-a)しておく? 継承は便利で使いやすい。 コード(ノード)も少なくなる。 ついつい継承を使ってしまう人も多いですが。
オブジェクト指向で良くある論議 一般論で言えば包含(合成)の方が都合が良いと言われます。 『クラスの「継承」より「合成」がよい理由とは? ゲーム開発におけるコードのフレキシビリティと可読性の向上』 http://postd.cc /why-composition-is-often-better-than-inheritance/ では包含でいいのでは?
UE4で包含させる
コンポーネントを使う UE4には多才なコンポーネント があります。 これらをアクターに追加する事 で簡単に包含を実現可能。 明確に機能を分離する。
では継承は悪なのか? これもよく言われますが、継承を使うことで オブジェクトの多態性などが実現できます。 ただしなんでもかんでも継承するのはよくない。
なんでもかんでもコンポーネント これはこれでマズい!! コンポーネントを追加するという事は アクターを1つ追加するという事と 実はあまり大差がなかったりする。
コンポーネントの問題 Epic Games Japan 岡田さんの過去スライドより https://www.slideshare.net/EpicGamesJapan/robo-recallvr
コンポーネントの問題 Epic Games Japan 岡田さんの過去スライドより https://www.slideshare.net/EpicGamesJapan/robo-recallvr
コンポーネントコスト コンポーネントを増やすという事は、 それだけ高いコストをし払うという事。 特に大量に出現するようなアクターでの コンポーネントは最低限にしましょう。
ケースバイケース
どう使いわけるか? ■ 継承が有利に働くケース ・大量に出現するキャラの共通処理を作る ・継承クラスでの多態性を実現したい時 ・クラスの親子関係を持ちたい場合
どう使いわけるか? ■ 包含が有利に働くケース ・クラス機能を明確に分割可能な時 ・多種多様なクラスで汎用処理を使いたい ・複雑な依存性を作りたくない
クラス設計って大変… ┐(-。-;)┌
AIキャラクタークラス
AIキャラクタークラス プレイヤーキャラクタークラスよりも 圧倒的に数が多いのが、このAIキャラクタークラス。 敵だけではなく、 汎用NPCでも同様。
AIについて 過去に登壇した時に使ったこのスライドが UE4のAIについて簡潔にまとめているのでおすすめ。 『はじめてのAI~ 愛のあるAIを作ろう』 https://www.slideshare.net/masahikonakamura50/ai-ai-62023284
AIについて ろっさむさんが書かれた、こちらの記事もおすすめ! 『【UE4】味方AIの作り方!AIとは何かを学びながら、 ブループリントで味方キャラクターを実装しよう』 https://qiita.com/4_mio_11/items/9e8af2ce82ee2a7625b5
AI概略図(抜粋)
やくわり分担 ・Characterクラス(見た目&コリジョン担当) ・Animationクラス(アニメーション担当) ・AI Controllerクラス(AI制御担当) ・Behavior Tree(AIフロー担当) ・Behavior Treeタスク(AIロジック担当) ・Blackboard(AIデータベース担当) ・AI Components(AI共通コンポーネント担当)
クラス基礎設計 クラス基盤部分は多くの場合、共通なことが多い。 設計段階で共通部分を抜きだす。 ・プロパティ部分(Health, Speedなど) ・メソッド部分(Init, Action, Moveなど)
クラス基礎設計 AIの場合、他にも共通部分が多々ある。 ・AI Controller ・Behavior Tree ・Blackboard
汎用タスクを作る UE4でAIを作る上で最も効率的に 使い回しやすいのが、ビヘイビアツリータスク。
汎用タスクを作る時のポイント 基盤部分のクラスメンバー以外は使わない。 一方的にビヘイビアツリーから参照されるのみにする。
汎用タスクを作る時のポイント もしクラス固有機能が使いたければ、 必ずインターフェースを経由する。 依存性さえなければ、そのタスクはあらゆる部分で 使いまわすことが可能となり、バグも発生しない。
やりすぎない なんでもかんでも汎用化すればいい というものでもないということを念頭に置く。 使い勝手の悪いクラスやコンポーネントを用意しても 最後まで使わないという事は多々あります。
複雑過ぎる処理 ボスキャラクターなど複雑でスペシャルな処理が 必要なキャラクターは特に汎用化が難しい。 そういう時は汎用化の方が 面倒なので専用タスクにしましょう!
イベントドリブン設計
イベントドリブン設計 UE4のAIシステムはイベントドリブン(駆動)で 動かせるように最初から設計されています。 イベントが発生するまでは何も処理させない。 イベントが来て始めてAIは行動する。
Tickを使わない みんな大好きEvent Tick 。 厳密に言えばこいつもイベントですが、 CPU負荷が簡単に高くなってしまいがちだったり、 複雑な設計の場合には、処理がすぐ煩雑になる。
Behavior Treeはイベントドリブン Behavior Treeはタスク、デコレーター、サービスといった ノードを使う事で、イベントドリブンに処理されます。 イベントドリブンに処理する事によって、 CPU処理負荷は最低限のものに。 サービスの場合は呼び出しインターバルを 秒単位で設定する事も出来ます。
AIのイベント AI Perception Componentを使うと、 AIのためのイベントを沢山取得できるようになる。 ・視覚(発見時や消失時) ・聴覚(音発生や寿命) ・痛覚や位置予測なども
AIのイベント AI Perception Componentの素晴らしき記事。 『【UE4】AI Perception の紹介と使い方』 https://qiita.com/Dv7Pavilion/items/741402f4da609ec595a2 『【UE4】AI Perception の紹介と使い方 その2』 https://qiita.com/Dv7Pavilion/items/2984f2b03711492aa451
まとめ
キャラクタークラス設計まとめ ・インターフェースでカプセル化しよう ・継承と包含はケースバイケースで使いわけよう ・AIクラスは汎用化してタスクなどを上手く分けよう ・イベントドリブンで設計しよう