めざせお手軽で富豪的なSTGアーキテクチャー(8)

ステージに対する敵の配置・制御方法について。
STGではゲームの進行状態(開始からの秒数とかスクロール量)によって敵が画面上に現れます。
また、敵は自分の状態に従って弾を出したりします。
この二つの処理は「ステージ上に敵を配置するもの」もオブジェクトとみなすことでまったく同じものとして扱えます。では、その「処理」をどのように表すか、が今回のテーマです。
とりあえず便宜上敵や弾を生成したり時間経過を待ったりする処理のことを「アクション」と呼ぶことにします。オブジェクト指向的にはアクションのベースクラスを継承して具体的な生成・待機のアクション(コンクリートアクション)クラスを作成し、それが持つアクション実行の操作を使ってアクションを実行することになります。アクション実行インターフェースには誰のアクションを実行するのか、ということを伝えるための引数*1が必要なので、それをIActionSubject型とすると以下のようになります。

public class ActionBase {
    public virtual bool Action(IActionSubject s)
    { return true; }
}
public class BulletShootAction : ActionBase { ... }

IActionSubjectはインターフェースとします。すでに存在するGmObjectクラスの派生オブジェクトがアクションを実行するために備えなければならないので、IActionSubjectがクラスだと多重継承になってしまうためです。C#は多重継承を使用できません。
こいつをGmObject派生クラス(敵を表すGmEnemyクラス)に登録するために

public class GmEnemy : GmObject, IActionSubject {
    public ActionBase Action{get; set;}
}

とでもしておけば、あとは制御タスク(ControleTask)内で Action.Action(this); を呼び出せばよいことになります。Compositパターンと呼ばれる手法を用いることで、GmEnemy側ではActionを配列として持つなどという面倒なことはしなくてよくなりますので、今回はそれを採用し、GmEnemyはアクションをひとつだけ登録可能、としました。こうしておかないと、「生成時アクション」とか「死亡時アクション」あるいは「ダメージ時アクション」、「部品破壊時アクション」などを追加しようとするたびにGmEnemyに配列が増えていって大変なことになります。

*1:ActionBaseのコンストラクタで渡すかもしれません