Unity 6の2Dでプレイヤーを追いかける敵を作る(Vector2.DistanceとMoveTowards)

広告

この記事は「Unity 6 2D移動の総合ガイド」の一部です。前の記事では、複数の地点を巡回しました。今回は、プレイヤーに近づくと追いかけてくる、敵キャラクターの動きを作ります。

この記事で分かること

  • Vector2.Distance()で、相手との距離を調べる方法
  • 距離に応じて、動く・動かないを切り替える方法
  • Vector2.MoveTowards()で、Z座標を変えずに追いかける方法

1. 完成する動き

  • プレイヤーとの距離が5以内になると、敵が追跡を開始する
  • プレイヤーに1まで近づくと、敵が停止する
  • プレイヤーが遠くにいるときは、敵は動かない
追いかける側の四角が、プレイヤーの四角へ向かって近づいていく動き

2. 準備

共通の準備は総合ガイドの「共通の準備」をご覧ください。

① プレイヤーを用意する

Playerに、③ 8方向に移動するで作ったEightDirectionMoveを取り付けます。他の移動スクリプトが付いている場合は、取り外してください。このスクリプトを使うと、WASDキーでPlayerを動かせます。

② 敵を用意する

Hierarchyで2D Object → Sprites → Squareを作り、名前をEnemyにします。Playerと区別できるように、別の色(例:赤)にし、Playerから少し離れた位置(例:(6, 0, 0))に置きます。この記事もTransformを直接動かす方法なので、Rigidbody 2Dは不要です。

Z座標は、PlayerとEnemyのどちらも0にしておきます。

3. スクリプトを作る

ChaseMoveという名前でスクリプトを作り、Enemyに取り付けます。

ChaseMove.cs

using UnityEngine;

public class ChaseMove : MonoBehaviour
{
    [SerializeField] Transform player;

    [SerializeField] float speed = 3f;
    [SerializeField] float chaseRange = 5f;
    [SerializeField] float stopDistance = 1f;

    void Update()
    {
        if (player == null)
            return;

        float distance = Vector2.Distance(
            transform.position,
            player.position
        );

        if (distance > chaseRange)
            return;

        if (distance <= stopDistance)
            return;

        Vector2 newPosition = Vector2.MoveTowards(
            transform.position,
            player.position,
            speed * Time.deltaTime
        );

        transform.position = new Vector3(
            newPosition.x,
            newPosition.y,
            transform.position.z
        );
    }
}

InspectorのPlayer欄に、HierarchyのPlayerをドラッグして登録します。ゲームを再生して、WASDキーでPlayerをEnemyに近づけてみましょう。

4. コードの解説

① 3つの数値

変数意味初期値
speed追いかける速さ3
chaseRange追跡を開始する距離5
stopDistanceこれ以上近づかない距離1

② 距離を計算する

float distance = Vector2.Distance(
    transform.position,
    player.position
);

Vector2.Distance()は、2点間の距離を計算します。その結果を使うことで、距離に応じて、動きを切り替えられます。

ここではVector2版を使っているため、Z座標の差は距離に含まれません。transform.positionはVector3ですが、自動的にVector2へ変換されます。

③ 距離による分岐

if (distance > chaseRange)
    return;

if (distance <= stopDistance)
    return;

距離によって、次の3つの状況に分かれます。

距離状況敵の動き
5より遠い追跡範囲の外動かない
1より大きい5以下追跡範囲の中近づく
1以下近すぎる動かない(停止)

このように、returnで処理を途中で終える書き方を使うと、条件が重なっても読みやすくなります。

④ Vector2.MoveTowards()とZ座標

transform.position = new Vector3(
    newPosition.x,
    newPosition.y,
    transform.position.z
);

Vector2で計算した結果はXとYだけです。そのため、Vector3に戻すときに、元のZ座標をそのまま残して代入します。前の記事で紹介した「Z座標をそろえる」という注意点を、この書き方で避けられます。

5. このプログラムの限界

このプログラムは、プレイヤーの方向へまっすぐ進むだけです。間に壁があっても、壁を避けず、ぶつかっても止まりません(Transformを直接動かすため)。壁を避けながら追跡したい場合は、「経路探索」という仕組みが必要になります。経路探索は、総合ガイドの「さらに学びたい移動の仕組み」で紹介しています。

6. よくあるつまずき

症状確認すること
敵が動かないInspectorのPlayer欄に登録したか。Playerが追跡範囲(5)の外にいないか
Playerが動かないPlayerにEightDirectionMoveが付いているか
敵がPlayerに重なるstopDistanceが0になっていないか
画面外へ出てしまうすべてのオブジェクトのZ座標がそろっているか

7. 練習問題

  1. speed、chaseRange、stopDistanceを変更して、動きを比べてみましょう
  2. 追跡範囲をSceneビューの円で表示して、目で確かめられるようにしてみましょう
  3. プレイヤーの速さ(5)より、敵の速さを速くしたときと、遅くしたときの違いを確かめてみましょう

ヒント(2つ目)

  • OnDrawGizmosSelected()という特別なメソッドを使うと、オブジェクトを選択している間だけ、Sceneビューに図形を表示できます。
  • 円を描くには、Gizmos.DrawWireSphere()を使います。

解答例:追跡範囲を表示する(まず自分で試してから見ましょう)

ChaseMoveのクラスの中に、次のメソッドを追加します。

void OnDrawGizmosSelected()
{
    Gizmos.color = Color.yellow;
    Gizmos.DrawWireSphere(transform.position, chaseRange);

    Gizmos.color = Color.red;
    Gizmos.DrawWireSphere(transform.position, stopDistance);
}

Enemyを選択すると、Sceneビューに黄色の円(追跡範囲)と赤い円(停止距離)が表示されます。この表示はSceneビューだけです。Gameビューや完成したゲームには表示されません。

8. まとめ

  • Vector2.Distance()で、相手との距離を計算できる
  • 距離に応じて、動く・動かないを切り替えられる
  • Vector2.MoveTowards()を使うと、Z座標を気にせずに追いかけられる
  • このサンプルは壁を避けない

次の記事では、マウスでクリックした場所へ移動します。

訪問数 1 回, 今日の訪問数 1回

広告