デバッグコマンド

注:今回紹介するコンポーネントはデバッグサンプルに入っています。

debugcmd

開発中にいろんなものを実行したい

ゲーム開発中には様々な情報が欲しくなる場面が沢山あります。シンプルな情報であればブレークポイントを設定して変数を調べたりすることができますが、3Dゲームで画面に表示されている数十体もある敵のうちからひとつの敵の情報をデバッガを使って得るのは大変です。また、スタンドアローン実行している状態、例えばVisual StudioからCtrl+F5で実行した時や、友達の家でテストプレイしているときにバグが発生したときにも、ある程度の情報を知りたい場面があります。それ以外にも開発中に実行したいことはパッと思いつくだけでも以下のものがあります。

  • シーン全体のポリゴン数
  • 指定したシェーダーのみの表示
  • カメラの視錐体表示してビューカリングのチェック
  • シーン内を自由に移動できるデバッグ用カメラへの切り替え
  • 敵の数の表示
  • シーン内のコリジョン情報の表示
  • イベントトリガー等の表示
  • AIテスト用に任意の敵を出現させる
  • 任意の敵を即死させる
  • AIステートの表示
  • 指定した武器を出現させる
  • 指定したステージへの移動
  • プレイヤーのHP設定
  • Godモード (無敵モード)

これだけの機能を実現するシステムとして、以下の方法が考えられます。

  1. 欲しい機能ごとにソースコードを変更してコンパイルする
  2. コントローラーのボタンに割り当てる
  3. 簡単なデバッグメニューを作る
  4. WindowsみたいなUIを表示させるAPIを作る

1は単純な方法ですが、いちいちソースコードをコンパイルするというのは面倒だし、スタンドアローン実行時には使えません。2は表示、非表示といったことには使えますが、数に限りがあり管理するのも大変です。3も表示のコントロールなどには使えますが、任意の敵を指定した場所に出現させるといったことには不向きです。4については、Windowsの場合は既存のUIを使えば良いのですが、Xbox 360上では自作する必要があるし、仮にそういったAPIがあったとしても機能ごとのGUIを作るのに手間が掛かります。また、GUIの弱点としてはバッチ処理をするのが難しいという面もあります。

デバッグコマンド

こういった要求を満たすためにゲーム開発現場で昔から使われているのがデバッグコマンドです。本来は開発時のみの機能で商品化されたゲームで見かけることは少ないのですが、PCゲームではDOOM時代からあるものなので知っている人も居ると思います。

デバッグコマンドの利点は文字列を入力実行し、出力するというシンプルさにあります。ゲーム中にコマンドを入力して実行するのはもちろん、ネットワークを介して文字列を転送して実行することができます。このことを利用して開発PCとXbox 360間をNetworkSessionで繋ぎ、Windows上ではGUIを使い、その情報を文字列にしてXbox 360に転送するという使い方もできます。この手法はPC上で作ったマップエディターから実機上への通信にも利用されています。特にPC画面上と開発機から出力された画面では色が違うのでBrute Force開発時にはライティングの最終調整などで使っていました。

デバッグコマンドを使う

サンプルではDebugCommandUIコンポーネントを使用します。DebugCommandUIコンポーネントはDebugManagerにあるフォントを使用するので、DebugCommandを追加する前にDebugManagerコンポーネントを追加する必要があります。

 // デバッグマネージャーの初期化と追加
debugManager = new DebugManager( this );
Components.Add( debugManager );

// デバッグマコマンドUIの初期化と追加
debugCommandUI = new DebugCommandUI( this );

// デバッグコマンドUIを最上面に表示させる為にDrawOrderを変更する
debugCommandUI.DrawOrder = 100;

Components.Add( debugCommandUI );

DebugManagerコンポーネントを追加した後に、DebugCommandUIコンポーネントを生成、デバッグコマンドを最上面に描画させる為にDrawOrderを変更してから追加するだけで、デバッグコマンドを使用することができます。ゲーム中にTABキーを押すことでデバッグコマンドを表示してコマンドを入力することができます。

新しいコマンドを追加するにはDebugCommandUI.RegisterCommandメソッドを使います。このメソッドにはコマンド名、コマンドの説明、そしてコマンド実行じに呼び出されるデリゲートを指定します。デバッグサンプルではposコマンドを追加しています。

 // サンプルコマンドの登録
debugCommandUI.RegisterCommand(
    "pos",              // コマンド名
    "set position",     // コマンド説明
    PosCommand          // コマンド実行用デリゲート
    );

登録したデリゲートはコマンドが実行された時に自動的に呼び出されます。デバッグサンプルでは"pos"というコマンドが入力されるとPosCommandが呼び出されます。引数のhostはコマンドを実行したIDebugCommandHost、commandは入力されたコマンド文字列(ここではpos)、そしてargumentsは入力された他の引数を文字列の配列になっています。

 void PosCommand( IDebugCommandHost host, string command,
                                            IList<string> arguments )
{
    if ( String.Compare( arguments[0], "show", true ) == 0 )
    {
        // show オプションで現在座標を表示する
        host.Echo( String.Format( "Pos={0},{1}", debugPos.X, debugPos.Y ) );
    }
    else
    {
        // "pos x座標 y座標"と入力されたものを処理する
        debugPos.X = Single.Parse( arguments[0] );
        debugPos.Y = Single.Parse( arguments[1] );
    }
}

各コマンドは単なる文字列なのでテキストファイルとしてログを取っておいて、後で複数のコマンドをバッチ処理したりすることができます。Gamefest Japan 2008 デモプログラムの中ではデモを切り替えたときに以下のようにして3つのコマンドを呼び出していました。

 debugHost.ExecuteCommand( String.Format( "demo {0}", demoNames[curdemo] ) );
debugHost.ExecuteCommand( "fps on" );
debugHost.ExecuteCommand( "tr on reset log:on" );

デバッグコマンドは、その名が示すとおり、デバッグ時の機能なのでリリース時に外すことを忘れないようにしてください。Godモードのように無敵になるくらいだったらいいのですが、ゲームバランスを崩したり、ゲームの進行ができなくなるようなコマンドが残ったままリリースされるのは避けるようにしましょう。デバッグコマンドを外すもっとも簡単な方法は単にDebugCommandUIを追加しないようにするだけです。

と、いうわけでFPSカウンター、TimeRuler、そしてデバッグコマンドを紹介してきました。これら3つのコンポーネントをゲーム開発時に役立ててみてはどうでしょうか?