ホーム < ゲームつくろー! < デザインパターン習得編
Composite
〜入れ子の入れ子の入れ子の入れ子の・・・
@ オブジェクトか、箱か?
ゲームはパーツの集まりで出来ているといっても過言ではありません。しかし、そのパーツを良く見ると、さらに細かいパーツに分かれていることも良くあります。さらにさらに、その細かいパーツもまた細かく・・・と、再帰的に繰り返されている事が多分にあるのです。
例えば「シーン」という概念がゲームにはあります。ゲームはシーンの連続で、シーンの中には、さらに小さなシーンがあり・・・。こういう場合、シーンを管理するシーンマネージャが良く設定されたりします。
class SceneManager
{
protected:
vector<Scene> m_SceneAry;
public:
void AddSnene(Scene*);
void RemoveScene(Scene*);
};
さて、シーンマネージャはある場面のシーンを統括するとして、別のシーンに移る時はどうするか?別のシーンマネージャを作って入れ替えたりします。そうしていくうちにシーンマネージャは増えていきます。すると、管理しきれなくなるのでシーンマネージャのマネージャが出来たりして・・・。「あれ〜、同じシーンを管理しているのに変わりはないのに、マネージャクラスばかりが増えていく・・・」。
これは、シーンという「オブジェクト」とそれをたくさん入れる「箱」を区別してしまったために起きてしまいました。シーンマネージャが管理しているのも大きなシーンの1つに変わりありません。それであれば、いっそシーンクラスに「オブジェクト兼箱になってもらう」のが一番すっきりします。
class Scene
{
private:
vector<scene*> m_SceneAry; // 子シーン配列
protected;
Scene* m_pParent; // 親シーン
public:
virtual void AddScene(Scene*); // 子シーンを追加
virtual void RemoveScene(Scene*); // 子シーンを取り除く
virtual Scene* GetChild(int num){ // 子シーンにアクセス
if(m_SceneAry.size() < num)
return NULL;
return m_SceneAry[num];
}
virtual void Begin(); // シーン開始
};
このSceneクラスを見ると、親シーンと子シーンの両方を保持し、子シーンは追加したり取り除いたり出来る仕組みになっています。この「オブジェクト兼箱」という形で再帰的にオブジェクトを取り扱うパターンをCmpositeパターンと言います。
シーンの場合、親シーンから始まって、次々と展開していきますが、最終的には必ず最初のシーンに戻ることになります。それがたとえエンディングを終えたとしても、最初のシーンに戻ることは可能です。図示すると次のような樹状的な感じです。
これを見ると、例えばプロローグシーンはグッドシーン1とバッドシーン1を保持する「シーン」です。
Compositeパターンでは「箱」も「オブジェクト」も同じクラスから派生させますが、双方は実は区別されています。オブジェクトに当たるクラスでは、Add、Remove、GetChild関数の実装は空にして、子オブジェクトが無い事を保障します。一方、箱に当たるクラスでは、上記関数をすべて実装して、箱に徹します。
class LeafScene : public Scene
{
public:
virtual void AddScene(Scene*){}; // 子シーンを追加(空)
virtual void RemoveScene(Scene*){}; // 子シーンを取り除く(空)
virtual Scene* GetChild(int num){ return 0;}; // 子シーンにアクセス(空)
virtual void Begin(); // シーン開始
};
class CompsitScene : public Scene
{
public:
virtual void AddScene(Scene*){ // 子シーンを追加
m_SceneAry.push_back(Scenen);
}
virtual void RemoveScene(Scene*){ // 子シーンを取り除く
//* 取り除く実装 *//
}
virtual void Begin(); // シーン開始
};
LeafSceneは「枝葉」のシーンで、Begin関数によってシーンが開始されるとします。その代わり、子シーンを追加したり取り除いたりする機能はありません。一方CompositeSceneクラスでは、Begin関数によってシーンが始まるわけではなく、例えば保持している子シーンの選択画面になるとか、親シーンの情報から特定の子シーンのBegin関数を呼び出したりします。
Compsiteパターンの関係図は次のようになります。
基本的にはComposite派生クラスのOperationはchildrenとして登録されているOperationをすべて実行します。ただ、ここはシーンの例でもあったように、ユーザの自由でかまわないように思います。シーンの例に置き換えたのがこちらです。
この他にも、絵の中に絵があって、その中にまた絵があるような入れ子状の絵など、樹状構造になっているつながりがあれば、だいたいCompositeパターンを適用できます。