2. EasyStar.js の使い方

はじめに

敵キャラがスポーン位置からゴールまで移動するためのルートを探索します。 EasyStar.jsを使用しました。

基本概要

EasyStar.jsは、グリッドベースのマップでA*アルゴリズムを使用してパス検索を行うJavaScriptライブラリです。 タワーディフェンスゲームでは敵キャラクターの移動経路を算出するために使用されています。

findPathメソッド

easystar.findPath(startTile.x, startTile.y, endTile.x, endTile.y, callback)

このメソッドは以下の機能を持っています:

  • 開始座標から目標座標までの最短経路を計算
  • 結果はコールバック関数に配列として渡される
  • パスが見つからない場合はnullが渡される
  • easystar.calculate()を呼び出すことで実際の計算が実行される
easystar.findPath(startTile.x, startTile.y, endTile.x, endTile.y, (path) => {
    if (!path) return;
    this.spawnPoint = spawnPoint;
    this.basePath = path.map(
    (p) =>
        new Phaser.Math.Vector2(
        collisionLayer!.tileToWorldX(p.x) + map.tileWidth / 2,
        collisionLayer!.tileToWorldY(p.y) + map.tileHeight / 2
        )
    );
    // ...
});
easystar.calculate();

enableDiagonalsとenableCornerCutting

enableDiagonals

easystar.enableDiagonals();
  • デフォルトでは上下左右の4方向移動のみ許可
  • このメソッドを呼ぶと斜め方向を含めた8方向移動が可能になる
  • 例:S→Gに移動する場合に、→↓の2手ではなく、斜め1手で進む
    S□
    □G
  • disableDiagonals()で無効化できる

enableCornerCutting

easystar.enableCornerCutting();
  • 斜め移動時に障害物の角を通り抜けるかどうかの設定
  • 例:S→Gに移動。■=通行不可、□=通行可
    S□
    ■G
    S■
    ■G
    このような場合でも、enableCornerCutting()の場合はS→Gの移動が許可される。
  • disableCornerCutting()にすると障害物の周りを迂回する経路のみ選択される

update関数での処理

findPathで探索したルート(this.basePath)はマップタイル単位のルートですが、spriteはmatter(Physics)によって仲間や壁にぶつかり、微妙にルートをはずれます。そこで、updateで決められたルートの各ポイントに近づくように移動させます。

update() {
const ents = this.moveQuery(this.world);
for (const id of ents) {
    const sprite = this.sprites[id];
    const path = this.paths[id];
    const cursor = this.PathIndex.value[id];
    if (!sprite || !path || cursor >= path.length) continue;

    const target = path[cursor];
    const pos = new Phaser.Math.Vector2(sprite.x, sprite.y);
    const dist = target.clone().subtract(pos);
    if (dist.length() < 2) {
    this.PathIndex.value[id]++;
    continue;
    }

    dist.normalize().scale(this.Speed.value[id]);
    sprite.setVelocity(dist.x, dist.y);
}
}
  1. moveQueryで移動対象のエンティティを取得
  2. 各エンティティについて:
    • 現在のパス位置(PathIndex)から次の目標地点を取得
    • エンティティと目標地点間の距離を計算
    • 距離が2未満なら次のポイントへ進む(PathIndex++)
    • そうでなければ目標に向かって速度を設定

変数

basePath

private basePath?: Phaser.Math.Vector2[];
  • スポーン地点からゴール地点までの基本となる経路
  • EasyStar.jsによって計算されたパスをタイル座標から世界座標に変換したもの

paths

private paths: Record<number, Phaser.Math.Vector2[]> = {};
  • 各エンティティ(敵)ごとの移動経路を保存するオブジェクト
  • キーがエンティティID、値がVector2配列(移動経路)
  • 新しい敵が生成されるとbasePathをクローンして割り当てる

PathIndex

private PathIndex = defineComponent({ value: Types.ui16 });
  • 各エンティティが現在パス上のどの位置にいるかを示すコンポーネント
  • 目標地点に近づくとインクリメントされ、次の目標地点へ移動を開始
  • パス上を順番に進むための制御に使用

まとめ

EasyPath.jsを使って敵キャラのルート検索を行いました。