テラリウム徹底攻略ガイド

KAnimalクラスにあるメソッドのオーバーライド

泉 祐介+デジタルアドバンテージ
2002/06/21


 さて、FindNextPositionメソッドにより、次の経由点の座標が得られるようになったので、今度はこのメソッドを利用して移動するルーチンを記述する。

 KAnimalクラスでは、BeginMovingメソッドが呼ばれると目的地に向かって移動し、MoveCompletedEventイベント・ハンドラを利用して衝突したときに障害物を回避する、というスタイルをとっていた。しかし、新しい移動ルーチンでは見付かった障害領域を始めから避けるようにして移動するため、新たな障害領域が見付かった時点で経路を変更した方が効率的である。そこで、各ターンの最初に必ず呼び出されるLoadEventイベント・ハンドラをオーバーライドし、障害物の情報が更新されたタイミングで常に新しい経路を探索するようにする。同時に、BeginMovingメソッドは、目的地や移動速度を変更するメソッドという程度の扱いにする。

■経由する点を見つけて移動するMoveメソッド

 このメソッドは、動物の実際の移動を制御するメソッドである。先に取り上げたFindNextPositionメソッドを使って、得られた経由点への移動を指示していく。

private void Move() {
  if(Position == destination) {
    StopMoving();
    return;
  }
  Point bypass = FindNextPosition(Position, destination, 5);
  bypass = RangeCheck(bypass);
  BeginMoving(new MovementVector(bypass, moveSpeed));
}
経由する点を見つけて移動するKYAnimalクラスのMoveメソッド

 すでに目的地にいる場合はStopMovingメソッドによって移動を停止する。そうでない場合は、FindNextPositionメソッドから得られた経由点に移動を始める。一応、世界(フィールド)の外に移動しないように、KAnimalクラスのRangeCheckメソッドを使って世界の内部にある点を指定するようにしている。

 destinationとmoveSpeedはいずれもKYAnimalクラスで用意したフィールドで、destinationは目的地、moveSpeedは移動の速さを格納する。なお、このフィールドに格納するときには、KAnimalクラスのRangeCheckメソッドやSpeedCheckメソッドを使って有効な範囲内の値にしておく。RangeCheckメソッドやSpeedCheckメソッドについては前々回の記事を参照して欲しい。

■LoadEventイベント・ハンドラ

 先に述べたとおり、このイベント・ハンドラでは障害物の情報を更新する。

override protected
void LoadEvent(object sender, LoadEventArgs e) {
  try {
    base.LoadEvent(sender, e);
    UpdateObstacleArea();
    if(!destination.IsEmpty) {
      Move();
    }
  } catch(Exception exc) {
    WriteTrace(exc);
  }
}
KYAnimalクラスのLoadEventイベント・ハンドラ
このイベント・ハンドラによって、各ターンの最初で必ず移動に関する指示が行われる。

 まず、親クラスであるKAnimalクラスのLoadEventイベント・ハンドラを呼び出して、foundCreaturesなど、KAnimalクラス自身が持っているフィールドの値を更新する。次に呼び出しているUpdateObstacleAreaは、障害領域のリストを格納するfoundObstacleAreasフィールドの値を更新するメソッドだ。このfoundObstacleAreasフィールドの値はFindNextPositionメソッドで使っていることは先に述べたとおりである。そのあと、すぐ上で説明したMoveメソッドを呼び出し、必要に応じて経路が修正されるようになっている。ただし、目的地がまだ指定されていない場合は移動を行わない。

 UpdateObstacleAreaは次のようになっている。

protected void UpdateObstacleArea() {
  foundObstacleAreas = new ArrayList();
  foreach(OrganismState os in foundCreatures) {
    if(target != null) {
      if(os.ID == target.ID) continue;
    }
    foundObstacleAreas.Add(new ObstacleArea(os,State.CellRadius));
  }
}
障害領域の情報を更新するKYAnimalクラスのUpdateObstacleAreaメソッド

 ここでは、ほかの生物のリストを表すKAnimalのフィールドfoundCreaturesに格納されている値を基に、障害領域のリストを作成している。

 あとでも述べるが、targetフィールドにはBeginMovingメソッドで指定された生物(目標物)が格納されている。KAnimalクラスには、目的地の代わりに生物を指定するBeginMovingメソッドがあるということは前々回の記事で述べたとおりだ。この場合、目的地が指定された生物の座標として扱われる。当然、目的地はその生物の内部の点ということになるため、実際には到達できない点であり、経路も計算できなくなってしまう。それを避けるために、目標物は障害物として扱わない。targetフィールドはその判定のために使う。

■BeginMovingメソッド

 もともとは移動を開始するためのメソッドであるが、KYAnimalクラスでは、目的地や移動の速さを変えるためのメソッドとして使用している。実際に目的地や移動の速さを変更する部分は、ChangeMovementという名前のメソッドに分離した。まずはそちらを見ていくことにしよう。

private void ChangeMovement(Point point, int speed) {
  point = RangeCheck(point);
  speed = SpeedCheck(speed);
  if(point != destination || speed != moveSpeed) {
    destination = point;
    moveSpeed = speed;
    Move();
  }
}
目的地や移動の速さを変更するKYAnimalクラスのChangeMovementメソッド

 指定された位置、速さをKAnimalクラスのRangeCheckメソッド、SpeedCheckメソッドを使って有効な範囲にしたあとに、その時点での目的地(destinationフィールド)や移動の速さ(moveSpeedフィールド)と比較して、一方でも値が変化している場合はMoveメソッドを呼び出す。

 BeginMovingメソッドの方は、KAnimalクラスの同メソッドをオーバーライドして、このChangeMovmentメソッドを使うようにする。

override protected void BeginMoving(Point point, int speed) {
  target = null;
  ChangeMovement(point, speed);
}
override protected void BeginMoving(Point point) {
  target = null;
  ChangeMovement(point, FavSpeed);
}
override protected
void BeginMoving(OrganismState target, int speed) {
  this.target = target;
  ChangeMovement(target.Position, speed);
}
override protected void BeginMoving(OrganismState target) {
  this.target = target;
  ChangeMovement(target.Position, FavSpeed);
}
4種類のKAnimalクラスのBeginMovingメソッドをオーバーライド

 うしろ2つのオーバーロードでは、targetフィールドに指定された生物を格納し、上述したUpdateObstacleAreaメソッドでその生物を障害物から除外するように指示している。

■MoveCompletedEventイベント・ハンドラ

 この項目の最初で少し説明したが、KAnimalクラスでは、このイベント・ハンドラを使って障害物を回避していた。しかし、このイベント・ハンドラが呼ばれる前にLoadEventイベント・ハンドラが呼び出されるため、KYAnimalクラスでは、このイベント・ハンドラが呼ばれる前に経路の修正が行われる。そのため、このイベント・ハンドラでは移動に関する指示を行う必要がない。また、そもそも2種類の異なった障害物回避のロジックを同時に組み込むのは、思わぬ動作を引き起こす原因にもなる。

 そこで、このメソッドもオーバーライドし、KAnimalクラス独自の障害物回避を行わないようにする。具体的には、KAnimalクラスから移動にかかわる部分を取り除く。もとのKAnimalクラスのコードから新たに加えた部分はないので、変更後のコードだけを示しておく。

override protected
void MoveCompletedEvent(object sender, MoveCompletedEventArgs e) {
  try {
    if(e.Reason == ReasonForStop.Blocked) {
      OrganismState os = LookFor(e.BlockingOrganism);
      if(os != null) {
        if(IsBrunt(os) && CanAttack(os as AnimalState)) {
          Brunt = os as AnimalState;
          if(WannaAttack(Brunt)) {
            BeginAttacking(Brunt);
          }
          return;
        }
        if(IsFood(os) && WithinEatingRange(os)) {
          Food = os;
          if(WannaEat(os)) {
            BeginEating(os);
          }
          return;
        }
      }
    } else if(chase != null) {
      if(WithinAttackingRange(chase)) {
        chase = null;
      } else {
        BeginChase(chase);
      }
    }
  } catch(Exception exc) {
    WriteTrace(exc);
  }
}
KAnimalクラスのMoveCompletedEventイベント・ハンドラ
移動に関する指示は常にLoadEventイベント・ハンドラで行っているので、このイベント・ハンドラでは移動に関する指示を行わない。

KYAnimalクラスを使う

 では、いま作成したKYAnimalクラスを使った生物を作ることにしよう。とはいっても、KYAnimalはKAnimalを継承したクラスであるため、移動ルーチンが新しくなったことを除けばKAnimalと同じである。従って、基本的な使い方はKAnimalクラスと同じでよい。

 ここでは、前々回に作成した草食動物である“Tabata”を、KYAnimalクラスを使ったものに修正することにしよう。一応、生物には新しい名前を付けることにする。例によって、名前は山手線の駅名から付けるのだが、今回は“Gotanda”という名前にした。

 修正するといっても、実際にはクラス名と継承するクラスの宣言だけを変更するだけなので、前々回のTabata.csから書き換えるのはわずか2個所である。そのうちの1つは、アセンブリのアトリビュートでクラス名を指定している部分だ。具体的には、

[assembly: OrganismClass("Tabata")]

となっている行を、

[assembly: OrganismClass("Gotanda")]

に書き換える。もう1つは、実際のクラスの宣言を行っている部分で、

public Tabata : KAnimal

を、

public Gotanda : KYAnimal

に書き換える。

 書き換えが終わったら生物をビルドしよう。わずかな修正しか行っていないが、生物Gotandaはいま作成した新しい移動ルーチンに基づいて移動するようになっている。

 なお、ここで説明した書き換えをすでに済ませたソース・コードをダウンロードできるようにしておくので、興味のある読者は試していただきたい。

草食動物Gotanda(C#版)をダウンロードする
草食動物Gotanda(VB.NET版)をダウンロードする

 テラリウム徹底攻略ガイドは今回が最終回となる予定だったが、今回で予定していたもう1つのルーチンである、ダンスによる動物間通信が時間の都合で間に合わなかった。次回はこれについて解説する予定だ。End of Article

 

 INDEX
  [連載]テラリウム徹底攻略ガイド
  第6回 より高度な障害物回避
     障害物の扱い方
     障害物回避のためのアルゴリズム
     障害物回避アルゴリズムの実装
   KAnimalクラスにあるメソッドのオーバーライド
 
インデックス・ページヘ  「連載  テラリウム徹底攻略ガイド」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間