ホーム < ゲームつくろー! < デザインパターン習得編
State
〜オブジェクト指向で言うところの「継承」です
@ パターンの王道であり基本
Stateパターンは、オブジェクト指向の基本動作である「継承」のスタンダードな使い方を示したパターンです。継承はあるクラスの機能を受け継いだ派生クラスを作成することですが、派生クラスでは親クラスの一部の機能を変更できます。これは「仮想関数」を設ければ可能です。
ゲーム製作では、このパターンはありとあらゆるところに出現します。まさにパターンの王道、ゲーム製作の基本です。例えば、画面に敵キャラクタを置くとき、物を置く人(描画する人)はどんな敵が置かれるかは判断せず、単に「敵」を置くことに集中します。
class Enemy
{
protected:
int m_Code; // キャラクタコード
int m_x; // 横位置
int m_y; // 縦位置
public:
void GetPos(int *x, int *y); // 位置を取得
void SetPos(int x, int y); // 位置を設定
virtual void Draw(); // 描画
};
キャラクタコードには、敵1ならENEMY_1、敵2ならENEMY_2、中ボスならSUBBOSS_1、ボスならBOSS_1などが格納されます。それぞれの敵を派生クラスで作成する時に、描画の方法が異なるかもしれませんから、Draw関数は仮想関数になっています。
物を置く人(Drawer)は、次のようなコードになります。
// 敵キャラクタを配置
// Enemy配列にはEnemyクラスの派生オブジェクトへのポインタを格納
for(int i=0; i<NumEnemy; i++)
Enemy[i]->Draw();
Enemy配列に任意の派生クラスのポインタを格納すれば、「継承」の働きにより派生クラスのDraw関数が呼ばれるため、敵ごとに異なる描画が行われるようになります。これが、継承(仮想関数)の基本的な使い方ですし、Stateパターンのすべてです。
Stateパターンのクラス図を示します。
DrawerクラスとEnemyクラスで置き換えると次のようになります。
A Stateパターンの乱用は危険?
オブジェクト指向の基本機能である「継承」は、分かりやすくて凄く使いたくなります。しかし、派生を繰り返すことは、オブジェクトをどんどん具体化することになります。また、仮想関数がキーとなるので親クラスの関数の数が増えてしまうという欠点を持ち合わせます。派生先で使うかもしれないなどと考えて、意味の無い関数を親クラスに入れることがあります。しかし、たいてい、そういう関数は忘れ去られるのです。
派生のしすぎはクラスの数がべらぼうに増えるので管理自体も難しくします。SmallEnemy_Type_J_Redクラスという具体的なクラスを作ったとしても、SmallEnemy_Type_J_Buleクラスと色が違うだけなんて、意味の無い派生をしてしまうことは、少なくありません。クラスが増えればドキュメント(説明書)も増えるわけで、膨大なドキュメントを書き、後でひっくり返して読んだりと、作業効率も下がる一方です。
ということで、オブジェクト指向を学びだすと派生で物を考えたくなって使用がなくなるのですが、筆者の経験からして、クラスを派生しすぎるプログラムは管理面でひどい目にあいます。
Stateパターンは正に派生で共通の振る舞いを管理するパターンです。しかし、上の理由からその乱用は避けるべきでしょう。もちろん、これが有効に活用できる場面は山ほどありますから、その見定めが重要になる事は言うまでもありません。