<戻る

STGつくろー!
NO11 全体を動かす「ステージ管理人」登場


@ そろそろ欲しくなってくる人

 これまで自機や敵などのパーツをクラスで作ってきました。動きも大雑把に実装しています。こうなると欲しくなるのが全体を動かしてくれる人、すなわちステージ管理人です。

 ステージ管理人は、登録された自機、敵、弾、その他のオブジェクトの「GoNext関数」を実行するのが主な仕事です。GoNext関数を持っているのは皆CObjTimeクラスからの派生クラスです。この親クラスの派生オブジェクトだけを扱うわけです。

 ステージ管理人のToDoリストはこうなります。

ToDoリスト
・ ステージ管理人は動くオブジェクト(CObjTimeクラスの派生オブジェクト)を登録する
・ ステージ管理人は登録されたオブジェクトを1単位動かす


A ステージ管理人は動くオブジェクトを登録する

 さっそくテストコードを考えてみましょう。

StageManagerTest.h
  // キャラクタ登録テスト
void testRegistCharacter()
{
   // 管理人生成
   sp<CStageManager> spStageManager(new CStageManager);

   // テストオブジェクト
   sp<CEnemy> spEnemy(new CEnemy);
   sp<CBullet> spBullet1(new CBullet(0,0,0));
   sp<CBullet> spBullet2(new CBullet(0,0,0));

   // 動くキャラクタの登録
   spStageManager->Regist(spEnemy);
   spStageManager->Regist(spBullet1);
   spStageManager->Regist(spBullet2);

   // 登録されたキャラクタの数を取得
   assertEquals(0, spStageManager->GetRegistNum());
}

 コンパイルエラーを出してみると、「CStageManagerクラスがない」「Regist関数、GetRegistNum関数は知らない」というエラーが出ます。エラーを解消して行きます。

 まずCStageManagerは、オブジェクトを登録するRegist関数および登録されたオブジェクトの数を取得するGetRegistNum関数を持っています。「登録」なので、内部にリストを設けましょう。GetRegistNum関数はSTLのlistからそのサイズを取得するラッパ関数となります。

CStageManager.h
void CStageManager::Regist(sp<CObjTime> Obj){
   m_ObjList.push_back(Obj);
}

int CStageManager::GetRegistNum(){ return m_ObjList.size(); }

 テストはこれで通ります。シグナルをチェック変更してToDoリスト1段目はさっと終了です。



B ステージ管理人は登録されたオブジェクトを1単位動かす

 このテストも簡単ですね。リストに登録されているすべてのオブジェクトのGoNext関数を実行するだけです。

StageManagerTest.h
// キャラクタ移動テスト
void testMoveCharacter()
{
   // 管理人生成
   sp<CStageManager> spStageManager(new CStageManager);

   // テストオブジェクト
   sp<CEnemy> spEnemy(new CEnemy);
      spEnemy->SetMoveUnit(5.0);
      spEnemy->SetDirectionZY(45.0, 45.0);
   sp<CBullet> spBullet1(new CBullet(0,0,0));
      spBullet1->SetMoveUnit(10.0);
      spBullet1->SetDirectionZY(60.0, 60.0);
   sp<CBullet> spBullet2(new CBullet(0,0,0));
      spBullet1->SetMoveUnit(10.0);
      spBullet1->SetDirectionZY(-60.0, -60.0);

   // 動くキャラクタの登録
   spStageManager->Regist(spEnemy);
   spStageManager->Regist(spBullet1);
   spStageManager->Regist(spBullet2);

   // 登録されたキャラクタを1単位移動
   spStageManager->GoNext();

   // 各キャラクタの移動位置をチェック
   // 「1+」はレッドシグナル用
   POSITION expEnemy(1+2.5, 2.5, 3.535534);
   POSITION expBullet1(1+2.5, 4.330127, 8.660254);
   POSITION expBullet2(1+2.5, -4.330127, -8.660254);
   assertEquals( expEnemy,  spEnemy->GetPosition() );
   assertEquals( expBullet1, spBullet1->GetPosition() );
   assertEquals( expBullet2, spBullet2->GetPosition() );
}

このテストの内容自体は分かりやすいもので、登録されたオブジェクトをCStageManager::GoNext関数で1単位動かして、その位置を比較しているだけです。ただ、assertEquals関数がこれまでとちょっと違います。これまではint型、double型の比較だけでしたが、ここではオブジェクト自体を比較しています。これは、オブジェクトに比較演算子「==」が定義されていることを前提としています。POSITIONクラスにはこの演算子が定義されていないので、これはコンパイルエラーとなります。また、POSITIONオブジェクトの設定を楽にするために引数付きコンストラクタを追加します。以上を解消するプログラムを組むことになります。

 CStageManager::GoNext関数内は極めて簡単です。

CStageManager.h
void CStageManager::GoNext()
{
   list<CObjTime>::iterator it;
   for(it = m_ObjList.begin(); it!=m_ObjList.end(); it++)
      (*it)->GoNext();
}

 オブジェクトを直接比較するassertEquals関数をCTestCase親クラスに追加します。

CTestCase.h
template<class T>
void CTestCase::asserEquals(T &Obj1, T &Obj2)
{
   if(OBj1 == Obj2)
      OutputDebugString("□□Good !");
   else
      OutputDebugString("■■オブジェクトが異なっています");
}

 POSITIONクラスのコンストラクタは簡単なので割愛します。
 これでコンパイラは通りますし、テストも「+1」を取ればグリーンシグナルとなります。



C 今後に向けてのリファクタリング

 ここまでで登録したオブジェクトを動かすと言う部分までの基礎ができました。しかし、じっくりクラスの関係を見直してみると、もう一度見直しが必要な部分がいくつもあります。これは徹底的なリファクタリングを要します。これによって、プログラムがよりすっきりするんですから、楽しんでリファクタリングしましょう!
 次の章でリファクタリングを開始します。