クラスまみれのゲームプログラミング入門

17:複数のオブジェクトを扱うテクニック

 結論から言うと、タスクリストにExec()の実装していないオブジェクトを追加し、必要に応じてこのリストから呼び出せばいいことになります。と、いうことで、まずはリストの中から特定のオブジェクトを参照するための関数を作成することにしましょう。タスク追加時に用いた文字列と検索に用いるキー文字列と完全一致したならば、目的のCGameObjectと判断して、そのオブジェクトを返します。見つからなければNULL値を返すようにしています。

GameObject.h
/* 省略 */

class CGameObject
{
	/* 省略 */

public:
	/* 省略 */

	CGameObject* FindObject(char *name);

protected:
	/* 省略 */
};

GameObject.cpp
CGameObject* CGameObject::FindObject(char *name)
{
	list<ListData>::iterator i;
	for(i = objectlist.begin(); i != objectlist.end(); i++){
		if(strcmp((*i).name, name) == 0) return (*i).gameobj;
	}

	return NULL;
}

 複数の文字列をあつかうサンプルプログラムを紹介します。そのまえに覚えておきたいテクニックをもう一つ。現在この講座で公開しているタスク処理プログラムなら、Init()やExec()処理中に自分自身をリストから外させることができます。このテクニックを使えば、初期化処理専用のゲームオブジェクトを用意することができるので、プログラムの見た目もよりよくなるでしょう。

 それでは、初期化処理専用のクラス「CDxFontTestStart」と、動作処理専用のクラス「CMoveFont」を作成してみることにしましょう。

 CMoveFontでは座標位置(x,y)と移動方向(m)、それにCDxFontオブジェクトのポインタが内部データとして保有されています。タスクリスト登録時にCDxFontオブジェクトを検索し、データを連結します。連結するのは参照先だけなので、CDxFontオブジェクトは新規に作成されていないのところがミソです。

 一方、タスク処理リストからExec()コマンドが呼び出されるたびに、座標は上下方向にm分だけ移動し、上下の端にたどり着いたら方向転換が行われます。今回はサンプルプログラムなので、乱数生成関数rand()の乱数は常に一定です。必要に応じてプログラムのどこかにsrand()関数を追加して、生成法則を不定にするようにするとよいでしょう。

 初期化専用クラスCDxFontTestStartでは、ID3DXFontを格納したクラスをリストに追加し、先ほど解説したCMoveFontを横方向に並べるだけ並べています。

DxFontTest.h
#include "..\DxFont.h"

class CDxFontTestStart : public CGameObject
{
protected:
	void Init();
};

class CMoveFont : public CGameObject
{
public:
	void SetStartState(int pos_x, int pos_y, int speed);
private:
	int x, y, m;
	CDxFont *pFont;
protected:
	void Init();
	void Exec();
};

DxFontTest.cpp
#include "DxFontTest.h"
 
void CDxFontTestStart::Init()
{
	// フォントオブジェクトをリストに追加
	AppendObject(new CDxFont(), -1, "font", true);

	// 30ピクセル間隔で、移動するフォントのオブジェクトを配置
	CMoveFont *movefont;
	for(int x = 0; x < 640; x += 30){
		movefont = new CMoveFont();
		movefont->SetStartState(x, rand() % (480 - 16), rand() % 4 + 1);

		AppendObject(movefont, 1000 + x, true);
	}

	// 自分自身は必要なくなったので消去
	RemoveObject(this);
}

void CMoveFont::SetStartState(int pos_x, int pos_y, int speed)
{
	x = pos_x;
	y = pos_y;
	m = speed;
}

void CMoveFont::Init()
{
	// オブジェクトデータを参照として格納
	pFont = (CDxFont*)FindObject("font");
}

void CMoveFont::Exec()
{
	if(pFont){
		// 上下の端についたら方向転換
		if(y < 0 || y > 480 - 16) m = -m;
		y += m;

		pFont->Draw(_T("Text"), x, y);
	}
}

 CMoveFontは独立して動くので、クラスを追加した分だけ、おのおのが自由に動きます。大量の文字列が同時に動く様は圧巻です。クラスってわかると便利でしょ?