doom3 commentary
TRANSCRIPT
自己紹介• 海外ゲーム歴PC9801 版 Test Drive が最初?その後 Doom 、 Diablo 、 StarCraft 、 NetStorm から始まり、今ではSteam 、 Origin 、 iPhone で海外ゲームを遊んでいます
秋葉原で輸入ゲームも良く買ってます
Doom3 とは?• id software が開発した FPS• 2004/8 発売• マルチプラットフォーム
(Windows 、 Mac 、 Linux)• メインプログラマは John Carmack
• 2011/10 にソースコードが公開されたhttps://github.com/TTimo/doom3.gpl
Doom3 の特徴• 狭い通路• 懐中電灯と武器を切り替えるスタイル• ステンシルシャドウ• いくつかのカットシーン• スクリプトドリブンのレベルデザイン• コンソールウィンドウによるパラメータ
調整→id の伝統
John Carmack
• id Software 設立者の一人で、 FPS の生みの親
• wolfenstein 、 Doom 、 Quake、 Rage など数々のゲームを開発
• id Software のゲームはほとんど John Carmack が一人で書いていると言われている
Doom3 のコードを読んでみる• Visual Studio で開く→neo\doom.sln
• Visual Studio Express でビルドするためにはコードからATL を外す必要がある
→ チャレンジしてみましたがビルド出来ませんでした…
• ソースコードは GPL でもテクスチャなどのリソースは公開されていません。 Doom3 を買う必要があります
コーディングスタイル• C++• stl 、 boost は不使用• 例外、標準 RTTI は不使用• シンプルなテンプレート
→2004 年のコンパイラ事情や、 John Carmack が書いたコードということで、移植性と処理速度を重視していると思われる ( なにしろ Doom を書いたプログラマーであり…そもそも Quake3 までは C 言語だった )
コーディングスタイル• C++ の基本を押さえた書き方→const の徹底→ 最低限の virtual→ プリコンパイル済みヘッダー→ クラスの前方宣言
• レガシーな書き方も残っている→ 変数宣言を関数の先頭で行っている→ 名前空間を使っていない
idLib
• ゲームタイトルに依存しない処理を集めたライブラリ
• エンディアンスワップ• 色定義• タイマー• Key/value dictionary• ローカライズ用辞書• 汎用ファイルフォーマット
idLib
• ハッシュ (CRC32,MD4,MD5)• テキスト (CmdArgs,Base64)• コンテナ (2 分探索 ,btree, ハッシュテーブ
ル , リスト , リンクリスト , キュー , スタック , 文字列リスト , Vector Set …)
→STL の代わり
数学• ベクトル• 行列• 角度• クォオータニオン• 複素数• 乱数 ( 線形合同法 )• Lcp(Box Constrained Mixed Linear Complementarity
Problem solver)• Polynormal(Polynomial of arbitrary degree with real
coefficients)
ジオメトリ• DrawVert• JointTransform• Surface• Surface_Patch(Bezier patch surface)• Surface_Polytope( 多面体 )• Surface_SweptSpline• TraceModel( コリジョン用モデル )• Winding( 任意凸形状 )• Winding2D
プリュッカー座標系• a way to assign six homogenous coordinates
to each line in projective 3-space(wikipedia)らしいですが、詳細は理解していません。
• 衝突判定に 6次元座標を使う?
SIMD
• Simd_Generic で C++ の汎用実装をしつつ、一部関数を SIMD に置き換えている
• 以下の実装がある• 3DNow!• AltiVec• MMX• SSE• SSE2• SSE3(2004 年 Doom3 発売年 )
SIMD
• 処理負荷を計測する「テスト」が関数ごと用意されている
• 四則演算の他に TransformVerts などの描画演算や、 UpSamplePCMTo44kHz などのサウンド処理が実装されている
CurlLib
• Daniel Stenberg の cURL( クライアントサイド URL転送ライブラリ )
• http://curl.haxx.se
• ファイルシステムで使われている
エンティティ• ゲームオブジェクトはエンティティと呼ばれ、以下の種類がある
• idAnimatedEntity( アニメーションするもの。 Actor や Player)• idTestModel• idEntityFx• idAFAttachment(Articulated figure :多関節形状 )• idAFEntity_Generic• idCursor3D• idWeapon( 武器 )• idLight(光源 )• idBeam• idFuncEmitter• …
idTypeInfo主なフィールド• クラス名→ 文字列版と int 版が用意されている→int 版は通信対戦時のパケットに使う
• 継承元のクラス名• イベント ( メソッドを関数ポインタにしたも
の。後述 )• セーブ、ロード時に使う関数ポインタ
idClass主なメソッド• CreateInstance• GetClassname• ListClasses_f→ “Classname”, “Superclass”, “Type”, “Subclasses” の表示
• Spawn→ エンティティ出現時に呼ばれる
idClass イベント関連• PostEventMS( const idEventDef *ev, int time )• PostEventSec( const idEventDef *ev, float time )• ProcessEvent( const idEventDef *ev )
→各種イベント ( 関数ポインタ ) の実行。後述
idClass static フィールド• static int idClass::memused→ クラスインスタンスの総メモリ使用量
• static int idClass::numobjects→ クラスインスタンスの総数
フィールド初期化忘れ対策• idClass::CreateInstance→new するときにメモリ領域に 0xcdcdcdcd を書いておく
→ コンストラクタが走った後、メモリ領域に0xcdcdcdcd があるかどうかを調べて、フィールドの初期化忘れを見つける
Debug ビルドでのみ上記チェックを行う
idEntity• 様々な処理が集まっている
• thinking(物理更新や Update 処理など )• visuals( 描画処理 )• animation• sound• entity binding( エンティティの親子関係 )• physics• damage( 体力を減らしたり )• scripting( スクリプトの処理、シグナル管理 )• gui(GUI に命令を出す )• targets( 別のエンティティのシグナルを送る )
idAnimatedEntity
• idEntity にアニメーション (idAnimator) を加えたもの
• idAnimator→ 複数チャンネルのモーションブレンドや、親子構造が書いてあるが、詳細は割愛
idGameLocal• 様々なモジュールの集合クラス
• Entity( 全エンティティの管理 )• Random• Program( スクリプト )• Clipping• Physics• PVS(potential visible set)• MultiPlayerGame• SmokeParticles• カメラ• ゲームの種類などの汎用情報• …
idGameLocal
• 様々なメソッドを持つ
• Entity の出現、削除、検索• ネットワーク対戦情報• セーブ、ロード• ユーザー情報• Draw• ゲームのリセット• Printf• マップのロード• AAS(area system)
エンティティの検索• 名前で検索
• Key Value Dictionary で検索
const char * name;idEntity* ent = gameLocal.FindEntity( name );
const char * key;gameLocal.GetTargets(Key Value Dictionary, Entity, key );
エンティティの検索• クリッピング結果の取得
trace_t tr;// トレース結果用変数gameLocal.clip.TracePoint( tr, 線分の開始地点 , 線分の終了地点 , …);
idEntity* ent = gameLocal.GetTraceEntity( tr );// エンティティの取得
ent->Signal( SIG_TOUCH );// シグナルの発行ent->ProcessEvent( &EV_Touch, …);// イベントの実行
エンティティ生成• idTypeInfo で型指定してエンティティを生
成できる
worldModel = static_cast< idAnimatedEntity * > ( gameLocal.SpawnEntityType( idAnimatedEntity::Type, NULL ) );
イベントの登録• 各種エンティティの先頭で idEventDef を宣
言
• その後マクロで関数をイベントに登録する
const idEventDef EV_Weapon_Next( "nextWeapon" ); …
CLASS_DECLARATION( idAnimatedEntity, idWeapon )
EVENT( EV_Weapon_Next,idWeapon::Event_Next )
END_CLASS
イベントの呼び出し• idClass::ProcessEvent を呼ぶと、イベント登録された関数が呼ばれる
idEntity *other;other->ProcessEvent( &EV_Touch, this, &collision );
スクリプトからのイベント呼び出し
• スクリプトから idInterpreter::CallEvent でイベントを呼べる
• idWeapon::Event_Clear といったメソッドを直接呼び出さないでイベントを経由しているのはスクリプトのため?