Unity 6の2Dで指定した地点へ移動する方法(Vector3.MoveTowards)

広告

この記事は「Unity 6 2D移動の総合ガイド」の一部です。前の記事までは、プレイヤーがキーで操作する移動でした。ここからは、敵やNPCのように、プログラムで動くキャラクターの移動を作ります。最初は、指定した地点へ向かって移動する、いちばん基本の動きです。

この記事で分かること

  • Vector3.MoveTowards()で、目的地へ向かって移動する方法
  • 目的地をInspectorから登録する方法
  • 目的地を行き過ぎない理由

1. 完成する動き

ゲームを再生すると、オブジェクトが、あらかじめ置いたTargetの位置へ向かって移動し、到着すると停止します。

Targetに向かって移動し、到着すると停止する四角いオブジェクトの動き

2. 準備

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

① 移動するオブジェクトを用意する

この記事はTransformを直接動かす方法なので、Playerをそのまま使えます。ただし、前の記事で付けたRigidbody 2Dが残っていると、重力で落ちてしまうことがあります。次のどちらかにしてください。

  • Rigidbody 2Dと前の記事のスクリプト(JumpMove)を取り外す
  • 新しいSquareを作り、名前をMoverにする

② 目的地を作る

Hierarchyで右クリックしてCreate Emptyを選び、名前をTargetにします。空のGameObjectは見た目がないため、シーンビューでは座標だけが分かる目印として扱います。任意の位置(例:(5, 2, 0))に置きます。

位置が分かりにくい場合は、Inspectorの名前の横のアイコンをクリックして、アイコンをつけるとシーンビューで見やすくなります。

3. スクリプトを作る

TargetMoveという名前でスクリプトを作り、移動するオブジェクトに取り付けます。

TargetMove.cs

using UnityEngine;

public class TargetMove : MonoBehaviour
{
    [SerializeField] Transform target;
    [SerializeField] float speed = 3f;

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

        transform.position = Vector3.MoveTowards(
            transform.position,
            target.position,
            speed * Time.deltaTime
        );
    }
}

スクリプトを取り付けたあと、InspectorのTarget欄に、HierarchyのTargetをドラッグして登録します。ゲームを再生すると、オブジェクトがTargetへ向かって移動します。

このスクリプトはキー入力を使わないため、Input Systemの設定は関係ありません。

4. コードの解説

① [SerializeField] Transform target;

目的地を登録するための変数です。Transform型にすると、Hierarchyのオブジェクトをドラッグで登録できます。コードで座標を直接書くのではなく、シーン上で目的地をドラッグして調整できるのが利点です。

② if (target == null) return;

目的地が登録されていないときは、何もせずに処理を終えます。登録忘れでエラーが出続けるのを防げます。

③ Vector3.MoveTowards()

Vector3.MoveTowards(
    現在位置,
    目的地,
    移動できる最大距離
);

現在位置から目的地に向けて、「最大距離」だけ近づいた位置を返します。ここでは最大距離にspeed * Time.deltaTimeを渡しているので、毎フレーム、速さに応じた距離だけ進みます。

目的地に近づいても、基本的には指定した速さのまま進みます。そして、目的地を行き過ぎることなく、ちょうど到着できます。到着後はその場に止まります。

④ Lerpとの違い

目的地へ向かう方法には、Vector3.Lerp()もあります。違いを比べておきましょう。

MoveTowardsLerp
第3引数移動できる最大距離0〜1の割合
動き一定の速さで進む目的地に近づくほど遅くなりやすい(使い方による)
このシリーズでの扱いメインで使う必要に応じて紹介

一定の速さで目的地へ向かいたいときは、MoveTowardsが分かりやすいです。

5. 注意点:Z座標をそろえる

Vector3.MoveTowards()は、XとYだけでなくZ方向も含めて移動します。そのため、TargetとMoverのZ座標が異なっていると、Z方向にも動き、カメラの外へ出てしまうことがあります。

純粋な2Dゲームでは、すべてのオブジェクトのZ座標を0にそろえるのが基本です。ここでは、TargetとMoverのPositionのZがどちらも0になっていることを確認してください。

補足:Vector2版

Z座標を気にしたくない場合は、Vector2.MoveTowards()を使う方法もあります。

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

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

Vector2で計算してから、元のZ座標をそのまま残して代入します。この書き方は、後の「追いかける」記事でも使います。

6. よくあるつまずき

症状確認すること
何も動かないInspectorのTarget欄が空になっていないか。Speedが0になっていないか
落ちてしまうRigidbody 2Dが残っていないか
画面から消えるTargetとMoverのZ座標がそろっているか
目的地に近づいても震える他の移動スクリプトが付いていないか

7. 練習問題

  1. speedを変更して、動きの変化を確かめてみましょう
  2. ゲームを再生したまま、SceneビューでTargetをドラッグして動かしてみましょう
  3. 目的地に到着したときに、Consoleに「到着」と表示してみましょう

ヒント

  • 2つ目では、Targetの位置に合わせて、毎フレーム、目的地を読み直していることに注目しましょう。
  • 3つ目では、距離を調べるVector3.Distance()と、メッセージを表示するDebug.Log()を使います。

解答例:到着の表示(まず自分で試してから見ましょう)

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

    transform.position = Vector3.MoveTowards(
        transform.position,
        target.position,
        speed * Time.deltaTime
    );

    if (Vector3.Distance(
        transform.position,
        target.position) < 0.01f)
    {
        Debug.Log("到着");
    }
}

到着している間は、毎フレーム「到着」と表示され続けます。一度だけ表示したい場合は、「到着済み」を表す変数を用意するとよいです。

8. まとめ

  • Vector3.MoveTowards()で、一定の速さで目的地へ向かえる
  • 目的地を行き過ぎず、ちょうど到着できる
  • 2DではZ座標をそろえるか、Vector2版を使う

次の記事では、2つの地点を往復する移動を作ります。

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

広告