「GameComponent」タグのついた投稿

前に書いたGameComponentのメモの続きです。

今回は、Game1登録したらUpdate()だけでなくDraw()メソッドも自動で呼ばれるGameComponentの作り方を乗せます。

XNAには描画も出来るGameComponentが既に備わっています。GameComponentクラスの代わりに、DrawableGameComponentクラスを継承するとLoadContent()メソッドとDraw()メソッドをオーバーライドできます。Initialize()とUpdate()同様Game1内のbase.メソッド名()で順番に呼ばれます。

 

今回は前に作ったFPSカウンターのクラスで実装例を書きます。まず、FPSカウンターのクラスで、DrawableGameComponentクラスを継承し、コンストラクタも書きます。

class FPSCounter : DrawableGameComponent
{
float m_fps;    // 実際のFPS
float m_interval; // FPSの更新速度(殆どの場合は1秒)
float m_updateTimer; // 更新するまでを計るタイマー
int m_frameCount; // 現在のフレーム数

SpriteBatch m_spriteBatch;
SpriteFont m_font;

public FPSCounter(Game game)
: base(game)
{
m_fps = 0.0f;
m_interval = 1.0f;
m_updateTimer = 0.0f;
m_frameCount = 0;
}

次に、LoadContent()メソッドをオーバーライドします。

protected override void LoadContent()
{
m_spriteBatch = new SpriteBatch(Game.GraphicsDevice);
m_font = Game.Content.Load<SpriteFont>("Font");
base.LoadContent();
}

そして、Draw()メソッドをオーバーライドします。

public override void Draw(GameTime gameTime)
{
// フレーム数を増やす(目標は1秒に60回)
m_frameCount++;

// タイマーに前のフレームから過ぎた時間を加算する
m_updateTimer += (float)gameTime.ElapsedGameTime.TotalSeconds;

// タイマーが1秒を超えたら
if (m_updateTimer > m_interval)
{
// FPSを計算する, 速度が下がっていた場合はここで差を計算する
m_fps = m_frameCount / m_updateTimer;

// タイマーとカウンターをリセットする
m_frameCount = 0;
m_updateTimer -= m_interval;
}

// FPSの数値を描画する
m_spriteBatch.Begin();
m_spriteBatch.DrawString(m_font, string.Format("FPS: {0:F2}", m_fps), new Vector2(0, 0), Color.Black);
m_spriteBatch.DrawString(m_font, string.Format("FPS: {0:F2}", m_fps), new Vector2(1, 1), Color.White);
m_spriteBatch.End();

base.Draw(gameTime);
}
} // クラス実装終了

今回は紹介していませんが、GameComponent同様Initialize()とUpdate()メソッドもオーバーライド出来ます。

最後に、Game1クラス内で、FPSカウンターを登録します。

public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";

// Inputクラスをコンポーネントとして、登録する
Components.Add(new Input(this));

// FPSクラスを描画できるコンポーネントとして、登録する
Components.Add(new FPSCounter(this));
}

これで、Game1でDraw()メソッドを呼ばなくても、自動的にFPSが描画されます(SpriteFontは事前にプロジェクトに追加しましたので、そのままコピペしても動きません)。

今回は以上です。

今回は、GameComponentクラスを使用してクラスをプラグインっぽくする方法をメモします。

プラグインっぽく足すだけで動くクラスを作ることで、クラスのパーツ化を簡単にし、動作も自動化(っぽく)出来ます。さらに、付け足す時にはGame1クラスのコンストラクタに1行足すだけなので、複数人で作業するときにも便利です。

これを実装するには、GameComponentクラスを作製するクラスに継承させます。GameComponentクラスを継承したクラスはInitialize()メソッドと、Update()メソッドをオーバーライドできます。GameComponentクラスを継承したクラスのInitialize()メソッドはGame1クラスのInitialize()メソッドの後に呼ばれます。具体的には、base.Initialize()内でGame1クラスに登録されている全てのGameComponentのInitialize()メソッドが呼ばれています。Update()メソッドも同じです感じです。これにより、GameComponentを継承し、Game1クラスに登録されたクラスは、自動で動きます。

GameComponentクラスを使う例は、入力処理のマネージャークラス、プロファイリング用のクラスなど、色々とあります。

 

GameComponentクラスを継承したクラスを使うにはまず、クラス宣言時にGameComponentを継承します。継承したクラスのコンストラクタで、ベースクラスのコンストラクタを呼ぶのを忘れないください。このクラス(Inputクラス)のコンストラクタ内にGameクラスの引数があるのは、GameComponentが必要としているためです。

class Input : GameComponent
{
public Input(Game game)
: base(game)
{

}

次に、Initialize()メソッドとUpdate()メソッドをオーバーライドします。

public override void Initialize()
{
// ... このクラスの初期化
base.Initialize();
}

public override void Update(GameTime gameTime)
{
// ... 入力処理の更新
base.Update(gameTime);
}

クラスはこれで良いので、後はGame1のコンストラクタ内で登録するだけです。

public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";

// Inputクラスをコンポーネントとして、登録する
Components.Add(new Input(this));
}

以上です。説明がわかりにくい気がしますので (というか、下手すぎる orz)、解らないところはコメントください m_ _m