ゲームエンジンの中の話 釜井政義 <@i_love_falcom> 2015/12/1 モバイル・コンシューマ「開発比較」勉強会 #3
誰? ⚫ ⚫ ⚫ ⚫ ⚫ 釜井政義(@i_love_falcom) コンシューマでアクション系開発ばかり10数年 描画やシステム低層側実装が主な仕事 昔は(社内)インフラなんかもやってました バイク命
注意! ゲームエンジンが提供する機能は、 • Game Editor(+Tool群) • Run-time Framework 大きく分けてこの二つに別れますが、 今日はRun-time Frameworkの話をします
本日のお題 ⚫ ⚫ ⚫ ⚫ ゲームエンジンを理解する ゲームループ内で実行する処理 ゲームループ外の処理 まとめ
ゲームエンジンを理解する
ゲームエンジンを理解しようとするとき、 どこから手を付ければいい?
取りあえずソースコードとか読んでみる?
うん、無理!
関連技術を確認しながらならどう?
やっぱ、無理!
じゃあどこから理解する?
複雑でよく分からないなら、 基本に立ち戻ってシンプルに考える
ゲームループ 開始 Yes 終了? No 更新 描画 終了 フレーム同期
なぜゲームループ? ⚫ ⚫ ⚫ どんなRun-time Frameworkにも必ずある(はず) 最外の処理なので全体の流れを見るのに都合がいい 対象となるプラットフォームによって、目的のコードが何処 にあるのかある程度見当がつく ⚫ 例えばAndroidの場合、SurfaceViewを継承しているクラ スを探す等
古典的ループ CPU 更新 描画 GPU待ち 同期 GPU CPU待ち 描画
CPU-GPU並列ループ CPU GPU N Frame 描画 N Frame 描画 N+1 Frame 更新 同 期 N+1 Frame 描画 N+1 Frame 描画
2スレッドループ CPU1 N Frame 更新 N+1 Frame 更新 同 期 CPU2 N-1 Frame 描画 N Frame 描画初期化 N Frame 描画 同 期 GPU N-1 Frame 描画 N Frame 描画
2スレッド+ジョブ並列化ループ CPU1 CPU2 GPU CPU3~ N Frame 更新 N-1 Frame 描画 同 期 N-1 Frame 描画 CPU1/2発行ジョブ N+1 Frame 更新 N Frame 描画初期化 同 期 N Frame 描画 N Frame 描画
2スレッド+ジョブ +非同期コンピュート並列ループ CPU1 CPU2 N Frame 更新 N-1 Frame 描画 同 期 N-1 Frame 描画 GPU GPGPU CPU3~ CPU1/2発行ジョブ N+1 Frame 更新 N Frame 描画初期化 同 期 N Frame 描画 N Frame 描画
並列・非同期化の方向へ進化する事によって、 CPU/GPU使用率が向上! ⚫ ⚫ ⚫ 複雑なエンティティを大量に処理できるようになる 反面、入力~描画間レイテンシが増大 ⚫ 対戦格闘やシューティングなど、低レイテンシであることが 必須のゲームに向かない 描画で使用していない間、汎用計算リソースとしてGPUを使用 する事で、これまで出来なかった事が可能になった
ゲームループ内で実行する処理
ゲームループ内で実行する処理 ⚫ ⚫ ⚫ エンティティシステム(タスクシステム) ⚫ エンティティの生成と破棄、およびリソース管理 ⚫ エンティティ更新、休止管理 描画システム ⚫ 描画リソースの生成、破棄、解決処理 ⚫ 描画コマンドの発行、Kick ゲームステート ⚫ ゲームの状態・遷移管理 ⚫ ゲームシーン(ステージ)管理
エンティティ ⚫ ⚫ ⚫ ⚫ ゲーム内オブジェクト全ての基底 エンティティシステムによって生成・破棄の管理を行う 通常一意のIDを持ち、これによって他のエンティティから検 索、参照する機能を提供する エンティティ間を疎結合にするため、メッセージ通信機能を 提供する場合も多い
古典的なエンティティの例 class ClassicalEntity { public: void Register(); void Unregister(); 新しいエンティティを実装するには、 このクラスを継承して以下のように使う 1 2 3 4 const char * GetID(); ClassicalEntity & Find(const char * id); protected: virtual void Update(const float delta) = 0; virtual void Draw() {} 一意のIDを設定 更新処理をUpdateに実装 必要なら描画処理をDrawに実装 ClassicalEntity(const char * id) {} Register/Unregisterを使って自身の登録処理、 virtual ~ClassicalEntity() {} 登録解除処理を行う private: 5 Findを使って他エンティティを検索 std::string myId; };
古典的なエンティティの問題点 ⚫ ⚫ 機能追加を行うため、多重継承、菱形継承問題が発生してし まう可能性が高い 不要な機能も継承して使うため、本来必要のない処理が行わ れてしまう可能性がある
多重継承、菱形継承の例 Game Object Game Object Movable Object Collidable Object 乗りもの Animation Object 武器 Skeletal Object 戦車 Human Dog Cat
is-a関係からhas-a関係へ Game Object Component 1 * Mesh Renderer Transform Rigid Body Animation Controller
エンティティの更新処理 ⚫ ⚫ 更新処理は複数のステージに分ける事が一般的 ⚫ 更新タイミングによっての分類 ⚫ アニメーション処理前の更新 ⚫ 剛体物理処理後の更新 処理方法による分類 ⚫ ジョブを使った並列更新 ⚫ 他エンティティはRead参照のみ可 ⚫ 単一スレッドによる逐次更新 ⚫ 他エンティティをRead/Write参照可
描画システム ⚫ ⚫ ⚫ ⚫ 全エンティティから描画情報(メッシュなど)を収集 ⚫ 更新・描画の並列化を行う場合ダブルバッファ化 収集した描画情報が描画対象となるかを判定 各描画パス毎に描画コマンドを生成、GPUへKick ⚫ ジョブが使用可能なら並列に描画コマンドを生成 使用しなくなった描画リソースの生存管理 ⚫ リソースを保持するエンティティが破棄されても、GPU 側で使用中の可能性があるのでリソースは描画システムが 管理する
ゲームステート スタート タイトル ⚫ ⚫ ⚫ ⚫ ゲーム全体を管理するステート ステージ管理ステートなどを持つ場合、 ゲームステート下に階層的に持つ 各種イベントをここからトリガ ステートの内容をスクリプトで記述でき るようにしたエンジンも多い ステージ1 ステージ2 ステージ3 エンディング オプション
ゲームループ外の処理
ゲームループ外の処理とは、 描画と同期しなくない処理 ⚫ ⚫ ⚫ ⚫ ファイルのロード処理 各種リソースの初期化処理 ⚫ GPUリソースなどはゲームループと同期する必要がある サウンド処理 ネットワーク処理
ファイルのロード処理 ⚫ ⚫ ⚫ ファイルをロードしている間、ゲームループを止めるわけに はいかない為、非同期ロードが必須 ファイルは通常圧縮されているので、解凍処理も行う 解凍されたリソースはリソースマネージャへ登録、解決処理 などを行う ⚫ 解決処理とは、名前などを用いて外部のリソースとの関 連付けを行う処理
サウンド処理 ⚫ ゲームループが処理落ちしてしまった場合など、サウンドの 更新が同期しているとプチノイズ等が発生してしまう ネットワーク処理 ⚫ 通信データの送受信は、その時々のトラフィック状態によっ てレイテンシが大きく異なってしまうため、描画と同期する わけにはいかない
まとめ ⚫ ⚫ ⚫ ゲームエンジンの処理は複雑でよく分からないが、ゲームル ープを追う事で全体的な流れを理解しやすくなる ゲームループはCPU、GPUの利用効率を上げることで大量の エンティティを扱うことができるようになったが、レイテン シが犠牲になるという面もある ゲームループの内で処理を行うか、外で行うかは、主に描画 と同期する必要があるかどうかで決まる
更にゲームエンジンについて学ぶなら・・・
絶賛発売中
質問?