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. 練習問題
speed、chaseRange、stopDistanceを変更して、動きを比べてみましょう- 追跡範囲をSceneビューの円で表示して、目で確かめられるようにしてみましょう
- プレイヤーの速さ(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座標を気にせずに追いかけられる- このサンプルは壁を避けない
次の記事では、マウスでクリックした場所へ移動します。












ディスカッション
コメント一覧
まだ、コメントがありません