6.5K Views
January 17, 24
スライド概要
CEDEC2015 (Computer Entertainment Developers Conference 2015)で行われた講演、
『いまどきのゲーム制作環境:エディター群とそのバックエンド、開発スタッフ間のコミュニケーションの具体的な方法解説』
で使用されたスライドの後半パートです。
※本スライドには動画が含まれております。pptxファイルをダウンロードすることで動画込みでご覧いただけます
前半パートのスライドはこちらです。
https://www.docswell.com/s/CAPCOM_RandD/5RX4PJ-cedec2015
講演概要は以下のサイトをご覧ください。
https://cedec.cesa.or.jp/2015/session/ENG/3250.html
※CEDECの資料公開サイトCEDiLでも本資料が公開されています。
https://cedil.cesa.or.jp/
株式会社カプコンが誇るゲームエンジン「RE ENGINE」を開発している技術研究統括によるカプコン公式アカウントです。 これまでの技術カンファレンスなどで行った講演資料を公開しています。 【CAPCOM オープンカンファレンス プロフェッショナル RE:2023】 https://www.capcom-games.com/coc/2023/ 【CAPCOM オープンカンファレンス RE:2022】 https://www.capcom.co.jp/RE2022/ 【CAPCOM オープンカンファレンス RE:2019】 http://www.capcom.co.jp/RE2019/
いまどきのゲーム制作環境 エディター群とそのバックエンド、 開発スタッフ間のコミュニケーションの具体的な方法解説 後半パート 株式会社カプコン 技術開発室 市山裕介 株式会社カプコン 技術開発室 是松匡亮
なぜ新しい制作環境なのか? • 開発のさらなる大規模化 – 第8世代への移行 – メモリ、演算リソースも増えた – MT FRAMEWORK の制作環境では効率化にも限界が見 えてきた • 制作環境を大きく見直す必要に迫られた
アジェンダ • エディターの紹介と内部設計 • 共同編集前提のアセット設計 市山 裕介 Yusuke Ichiyama • テレメトリ • 安定した環境の配布 • C#スクリプトとC++リフレクション 是松 匡亮 Masaaki Korematsu
アジェンダ(是松パート) • テレメトリ • 安定した環境の配布 • C#スクリプトとC++リフレクション
テレメトリ
『テレメトリ』について • さまざまなログをサーバーへ保存する – ゲームのプレイデータ • プレイヤーの行動経路 • 配置したアイテムの拾得率 – プロファイリング情報 • CPUの処理時間 – ツールのエラー情報
従来の方式と問題点 • 方式 – NASにファイルとして保存 • 問題点 – ファイルシステム経由のアクセス • ゲーム機からアクセスすることが難しい – 内部フォーマットが統一されない • ビューワーを都度実装 – 一元管理されない
新しいエンジンの方式と改善点 • 方式 – データベースへの保存をエンジンとしてサポート • 改善点 – RESTful API経由のアクセス • ゲーム機からでもHTTPでアクセス可能 – 内部フォーマットをJSONで統一 • 汎用的なビューワーの提供 – リストアップや検索が容易
実装 • RESTful API受け入れ用のphpを設置 – JSON形式の文字列をセーブ, ロード – 新しいエンジンではJSONが標準フォーマット • MongoDBに格納 – JSONのまま格納可能 – 完全なACIDは不要 • セーブ, ロード時にユーザー側でドキュメントを確定
クライアント • ライブラリをC++, C#の形式で配布 – ネットワークに明るくなくても使えるように • 閲覧用のWebページも用意 – 汎用ビューワーとして利用
デモ
デモ
活用事例 • クラッシュレポート – コメント, スタックトレース, SVNリビジョン • 操作履歴の保存 – マージ回数のカウント, マージミス時の調査用情報 • ゲーム調整用のデータ収集 – プレイヤーの死亡位置や回収されたアイテムの箇所
アジェンダ(是松パート) • テレメトリ • 安定した環境の配布 • スクリプトとリフレクション
安定した環境の配布
安定した環境配布の必要性 • あってはならないやりとり – プログラマ vs プログラマ • 「trunk / masterのビルドが通りません」 – プログラマ vs 他セクション • 「エンジンがすぐにクラッシュします」 • 解決を待つ間、ゲーム制作は止まる – 問題を早期に発見して修正することが必要
継続的インテグレーション • Jenkinsを主軸とした運用 – ビルド – テスト – 配布 – ネイティブキャッシュ
Jenkins構成 Jenkins Linuxマシン ビルドPC Windows PCが6台 テスト機材 開発プラットフォームごとに1台
ビルドとテスト
ビルドとテスト • ビルド – SVNコミットがある度に実行 • テスト – 実用的なシーンでスモークテスト • 起動, 終了, メモリリーク, スクリプト互換性 • 『起動すらしない』を防止 • 『サンプルでは再現しません』の防止 所要時間は20分
テストスイート • テストを実行する基盤 – 求めていた形のものがなかったため内製 • プロセスデバッガとして実装 – テストケースをプロセスの状態に応じて分岐 – プロセスの状態変化も詳細にテスト • 結果はJUnit形式で出力
テスト結果
テスト結果
配布
MT FRAMEWORKの方式と問題点 • MT FRAMEWORKの配布方式 – バッチファイルによる取得 – NASから手動コピー – SVNによる取得 • 問題点 – アップデートを忘れて古いバージョンを使う – 更新内容を知ることが難しい
新しいエンジンの方式 • 新しい配布方式 – 専用クライアントによる取得 • エンジン起動時にチェック • 改善点 – アップデート漏れがない – 更新内容はSVNログを取得して表示
配布
実装 • 配布バージョンの登録 – テスト通過時に SVNリビジョンと実行ファイルのMD5を登録 • アップデートチェック 1. 手元の実行ファイルのMD5を計算 2. MD5からSVNリビジョンを参照 3. より新しいリビジョンがあれば通知
ネイティブキャッシュ
『ネイティブ』について • 編集時に使うデータ(中間データ)は冗長 – JSONなどのテキスト表現 – 未圧縮のテクスチャ • 実行効率のために変換を行う – float値であれば直接バイナリでfloatを保存 – テクスチャであればDXT圧縮やSwizzle化
MT FRAMEWORKの方法と問題点 • MT FRAMEWORKの方法 – 各自の開発実機で変換を行う • エンディアンの違い • 問題点 – ネイティブ化の待ち時間が長時間化 • 標準的なタイトルの1ステージで30分以上 • 開発の大規模化に伴ってファイル数は増える一方
バージョン管理システムの検討 • 問題点 – ネイティブはデータ構造が変化しやすい • コンバーターの機能追加や仕様変更 – 中間ファイルとネイティブは1:1に対応しない • 使われているコンバーターのバージョンが異なれば ネイティブは変化する – バージョン管理するほどのものではない • 中間ファイルさえあれば再現することが可能 • より軽量な手段が必要
新しい方法と改善点 • 新しい方法 – コンバートをすべてPCで行う • 高速なCPUと大容量のメモリ – サーバー上に変換済みのデータを配置 • 中間とネイティブの比較をMD5で行う • 改善点 – ネイティブ化の待ち時間は劇的に短縮 • 変換時間よりも転送時間の方が短い
実装 • Jenkins – Perforceコミット時フックでトリガ • 更新のあったファイルのみを変換 – “中間ファイルのMD5.ネイティブバージョン” • エンジン利用者 – 手元の中間ファイルのMD5とネイティブバージョン でファイルをサーバーから探す – 見つからないときだけローカルPCで変換
効果 • 懸念 – キャッシュをしてもヒットしないのではないか • データは数分単位で更新され続ける • キャッシュ構築が間に合わない • 事実 – 60%程度のヒット率 • キャッシュがなければ待ち時間は2倍以上 – 変換に時間を要するデータほど更新頻度が低い • テクスチャやモーションなどの素材
アジェンダ(是松パート) • テレメトリ • 安定した環境の配布 • スクリプトとリフレクション
C#スクリプトとC++リフレクション
スクリプト導入の動機 • C++で実装されたゲームのビルド時間 – 短くできても5分程度 – リビルドには分散ビルドでも10分以上を要する • 読み込むデータの量は増える一方 – 少しでもTATを短くするためには ゲームコードのスクリプト化が不可欠
スクリプト導入の課題 • スクリプトシステムの選定 – Lua, Python, C#, … • スクリプトとC++コードのバインディング – クラスの型やメソッド情報の公開 – C++にはリフレクションが存在しない
C++リフレクションと問題点 • RTTIが存在する – 型特性と型名を取得することだけが可能 • 問題点 – 型名からインスタンスを構築できない – クラスやメソッドの情報を標準化された形で 一元的に列挙することができない • 独自のリフレクションを構築する手法が知られている – Scott Wakeling (2001)「Dynamic Type Information」 Mark DeLoura 『Game Programming Gems 2』Charles River Media
リフレクションの手動構築 // C++プログラマが実装したいコード class Player : public Object { DECLARE_CLASS(Player, Object); public: void doSomething(u32 v); u32 getHealth(){ return mHealth; } void setHealth(u32 v){ mHealth = v; } private: u32 mHealth; };
リフレクションの手動構築 // C++プログラマが実装したいコード class Player : public Object { // プロパティ情報の登録 DECLARE_CLASS(Player, Object); void register_Player(Collection& c) { public: c += REFLECT_PARAM_U32(&Player::getHealth, &Player::setHealth); void doSomething(u32 v); } u32 getHealth(){ return mHealth; } void setHealth(u32 v){ mHealth = v; } private: u32 mHealth; };
リフレクションの手動構築
// C++プログラマが実装したいコード
class Player : public Object
{
// プロパティ情報の登録
DECLARE_CLASS(Player,
Object);
void register_Player(Collection&
c)
// スクリプトとバインド
{
void
call_Player_doSomething(ScriptContext&
context)
public: c +=
REFLECT_PARAM_U32(&Player::getHealth,
&Player::setHealth);
{
void doSomething(u32
v);
}
Player*
playerPtr
= (Player*)context.getArgumentIntptr(0);
u32 getHealth(){
return
mHealth;
}
playerPtr->doSomething(context.getArgumentU32(1));
void setHealth(u32
v){ mHealth = v; }
}
private:
u32 mHealth;
};
リフレクションの手動構築
// C++プログラマが実装したいコード
class Player : public Object
{
// プロパティ情報の登録
DECLARE_CLASS(Player,
Object);
void register_Player(Collection&
c)
// スクリプトとバインド
{
void
call_Player_doSomething(ScriptContext&
context)
public: c +=
REFLECT_PARAM_U32(&Player::getHealth,
&Player::setHealth);
{
void doSomething(u32
v);
}
// スクリプトへ公開
Player*
playerPtr
= (Player*)context.getArgumentIntptr(0);
u32 getHealth(){
return
mHealth;
} : Object
public
class
Player
playerPtr->doSomething(context.getArgumentU32(1));
void setHealth(u32
v){ mHealth = v; }
{
}
public void doSomething();
private:
public uint Health{ get; set; }
u32 mHealth; }
};
手動構築の問題点 • メンテナンスに労力を要する – C++コードの追加時に忘れられる – スクリプトシステムへの理解が必要 • 公開される機能が限定的になる – ゲームコードがC++で書かれてしまう – TATが長くなってしまう
解決策の検討 • マクロ展開による自動構築 – 通常のC++コード記述が妨げられる – 特殊な記法を忘れると公開されない – 根本的な解決には至らない • C++コードの抽象構文木から自動構築 – 通常のC++コード記述を妨げない – 理論的にはC++コードすべてが公開される – ゲームコードをスクリプトのみで構築可能
新しい方式 • ゲームランタイムのC++コードを解析 – ClangのASTConsumerで抽象構文木を取得 • Stephen Kennedy (2012) 『Robustification through introspection and analysis tools』 GDC 2012 – クラス, メソッド, コメント, 属性を列挙 • 明示的な非公開は属性で制御 class Player : public Object { public: ATTRIBUTE(Hidden); void doSomething(u32 v); };
C#コードの駆動 • C#コンパイラでCILコードへ変換 • 内製のCLI仮想マシンによる実行 – ECMA 335に準拠 – ソースレベルデバッグが可能 • パフォーマンス – Lua 5と比較して遜色ないパフォーマンス – 出荷ビルドではILコードをC++コードに変換
デモ
デモ
デモ
デモ
デモ
アジェンダ(是松パート) • テレメトリ • 安定した環境の配布 • スクリプトとリフレクション
ご質問は? • 本日の内容 – – – – – エディターの紹介と内部設計 共同編集前提のアセット設計 テレメトリ 安定した環境の配布 C#スクリプトとC++リフレクション • 謝辞 – 技術開発室メンバー
コミュニケーションツール
ビルドやテストの失敗通知 • メールによる通知は見逃されやすい – 通常業務のメールに埋もれる • チャットツールへ反映 – BOTがJenkinsジョブを監視 • Jenkins Remote access API • XMLもしくはJSONで情報を取得可能
BOTによる通知 • コミッタを名指しで通知
BOTによる通知 • アメもお忘れなく
BOTによる通知 • クラッシュレポート • 配布版の状況
BOTその他 • その他 – ニュース, 株価, 天気, おしゃべり機能 – 開発現場の雰囲気作り
附録
他のスクリプト言語との比較 • The Great Win32 Computer Language Shootout – http://dada.perl.it/shootout/ • Lua5やPythonと比較して遜色ないパフォーマンス 25000 20000 Series1 15000 Series2 10000 Series3 5000 0 1 2 3 4 5 6 7
ランタイム基本設計 MT FRAMEWORK(2004~) • 32bit向けの設計 – 現在は64bitに対応済み • C++03 新しいエンジン(201x~) • 64bit向けの設計 – 第8世代機に最適化 • C++11~ • 編集用のUI/ロジックを内包 • 編集用のコードを除去 • マルチスレッドに最適化 • マルチスレッドに最適化
モジュール設計 • 機能毎にモジュールとして分離 – 機能のOn/Offが可能 – ミドルウェアへの 置き換えが容易 Kernel( OS API, Algorithm, STL, Boost ) Core ( Reflection, Scripting , Debugging ) Rendering Physics Networking VFX AI GUI HID Timeline Game
オブジェクトの表現方法 MT FRAMEWORK 新しいエンジン GameObject Object Motion Component Mesh Mesh Component Player Behavior CharacterBase Player Enemy AI GameObject Motion Component Mesh Component Enemy Behavior AI Component
並列化のポリシー 新しいエンジン MT FRAMEWORK AI Player Update AI AI Player Behavior Behavior Enemy Update Motion Enemy Update Renderer バリア同期 CPU1 CPU2 CPU3 Enemy Behavior Enemy Behavior Motion Motion Motion Mesh Mesh Mesh CPU1 CPU2 CPU3