XNA Game Studio 2.0の細かな修正点

Shawn Hargreaves氏のブログから拝借+ちょっと補足説明

XNA Game Studio 2.0になって追加、または修正された細かな機能を紹介します。

GamePadState.IsButtonDownとIsButtonUpメソッドの追加

今まではコントローラーのボタンが押されているかの判定はButtons.A == ButtonState.Pressedと言う様に長いコードを書かなければいけませんでした。特に複数のボタンのいずれかが押されているケースを判定するのには以下のようなコードになってしまいました。

 // AボタンもBボタンもXボタンも全部ジャンプだ!!
    if (padState.Buttons.A == ButtonState.Pressed ||
        padState.Buttons.B == ButtonState.Pressed ||
        padState.Buttons.X == ButtonState.Pressed )
        Jump();

XNA GS 2.0で追加されたIsButtonDownとIsButtonUpメソッドを使うことによって以下のようにスッキリとしたコードを書くことができます。

 if ( padState.IsButtonDown(Buttons.A|Buttons.B|Buttons.X) )
        Jump();

 

Keyboard.GetState(PlayeIndex player)オーバーロードの追加

このオーバーロードメソッドを使うことで、Xbox 360コントローラーにつけることのできるチャットパッドからの入力を取得することができます。以前の引数なしのメソッドを呼んだ場合はPlayerIndex.Oneを指定するのと同じ動作をします。

 

Audio.Cueの再生が途切れてしまう問題の解消

これは、1.0では参照されていないCueのインスタンスがガーベージコレクションが発生した場合、音声の再生の途中に関わらずに破棄されてしまい、その段階で音声が途切れてしまうという不具合がありました。、2.0ではCue.Finalizer内で音声再生中はインスタンスが破棄されないようにすることで解決しています。

この問題は特に3Dオーディオを再生する場合、Cueに対してリスナーとエミッターを関連付けるときに

     Cue cue = soundBank.GetCue("fire");
    cue.Apply3D( listener, emitter );

のようなコードを書く必要があり、このcueのインスタンスを保持するというのが面倒な作業になっていました。

2.0ではこの問題を解決し、上のように書いた場合でも問題なく動作するようになりました。また、SoundBank.PlayCue(string name, Listener listner, Emitter emitter)メソッドが追加されたので、簡単に3Dオーディオをサポートした効果音を再生することができます。

フレームワーク内での不要なメモリ確保の削除

不要なメモリ確保をすることはガーベージコレクションの頻繁な発生につながり、結果的にパフォーマンス低下に繋がります。特に世代別GCを実装していないXbox 360上では大きな問題になります。

2.0ではフレームワーク内で不必要なメモリ確保している部分の修正をしました。

簡単なところではGamePad.GetStateSpriteBatch.DrawStringメソッド内でメモリ確保しないようになりました。SpriteBatch.DrawString内では、Dictionary<K,T> を使って実装していたのですが、キーがバリュータイプだと不要なメモリ確保がXbox 360上で発生することがありました。興味深いのはデバッガ使用時と、未使用時でJITが生成するコードに違いがあり、デバッガ上で実行させるとボクシングが発生することがあります。ですから、GCの発生の有無を調べる場合はF5ではなく、Ctrl+F5で実行することをお勧めします。

ModelMeshCollectionModelMeshPartCollectionModelEffectCollection、そしてModelBoneCollectionクラスのGetEnumeratorメソッドの実装がIEnumeratorを返す代わりに、Enumerator構造体を返すように実装されました。

これはモデルを描画するのによく使われるパターンとして

     foreach (ModelMesh mesh in model.Meshes)
    {
        foreach (Effect effect in mesh.Effects)
        {
            BasicEffect be = effect as BasicEffect;
            if (be != null)
            {
                be.Projection = proj;
                be.View = view;
            }
        }
        mesh.Draw();
    }

のようにforeachを多用します。この時、コレクションがIEnumeratorを返すと暗黙的なメモリ確保が発生してしまいます。
せっかく便利なforeach文なのに、GCを避けるためにわざわざfor文に書き換える必要がありましたが、2.0ではforeachを使ってもメモリ確保は発生しません。

SpriteFontのカーニングサポート

カーニング(Kerning)とは、英数字でWAITのような文字を表示したときに全体の見た目のバランスを取るためにAとWの間を意図的に狭めるような処理のことです。これはspritefontファイル内に <UseKerning>ture</UseKerning> を指定することで設定できます。spritefontファイルで何も指定しない場合はカーニング処理をしないので、1.0で作ったファイルをカーニングさせるのにはUseKerningエレメントを追加してSpacingの値を0することをお勧めします。