Unity 6の2Dで複数の地点を順番に巡回する方法(配列とインデックス)

広告

この記事は「Unity 6 2D移動の総合ガイド」の一部です。前の記事では、2つの地点を往復しました。今回は、地点をいくつでも増やせる巡回の仕組みを作ります。敵の巡回や、NPCの移動に利用できます。

この記事で分かること

  • 配列(Transform[])で、複数の地点をまとめて扱う方法
  • インデックスで、「今どの地点を目指しているか」を管理する方法
  • 最後の地点の次に、最初に戻る方法

1. 完成する動き

4つの地点を用意し、オブジェクトが順番に巡回します。

Point0
  ↓
Point1
  ↓
Point2
  ↓
Point3
  ↓
Point0に戻る
四角いオブジェクトが複数の地点を順番にたどって巡回する動き

2. 準備

共通の準備は総合ガイドの「共通の準備」をご覧ください。前の記事のPingPongMoveが付いている場合は、取り外してください。

① 4つの地点を作る

Hierarchyで空のGameObject(Create Empty)を4つ作り、次のように配置します。

名前Positionの例
Point0(-4, 3, 0)
Point1(4, 3, 0)
Point2(4, -3, 0)
Point3(-4, -3, 0)

四角形を描くように並べると、巡回の動きが分かりやすくなります。Z座標はすべて0にします。

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

Squareを使い、PositionをPoint0と同じ位置にしておきます。この記事もTransformを直接動かす方法なので、Rigidbody 2Dは不要です。

3. スクリプトを作る

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

PatrolMove.cs

using UnityEngine;

public class PatrolMove : MonoBehaviour
{
    [SerializeField] Transform[] points;
    [SerializeField] float speed = 3f;

    int currentIndex = 0;

    void Update()
    {
        if (points == null || points.Length == 0)
            return;

        Transform target = points[currentIndex];

        if (target == null)
            return;

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

        if (Vector3.Distance(
            transform.position,
            target.position) < 0.01f)
        {
            currentIndex++;

            if (currentIndex >= points.Length)
            {
                currentIndex = 0;
            }
        }
    }
}

Inspectorで地点を登録する

スクリプトを取り付けると、InspectorにPointsという欄が表示されます。

  1. Pointsを開く
  2. Sizeを4にする
  3. 表示されたElement 0〜3に、Point0〜Point3を順番にドラッグして登録する

登録する順番が、巡回する順番になります。ゲームを再生すると、四角形を描くように巡回します。

4. コードの解説

① 配列:Transform[] points

配列は、同じ型のデータを並べてまとめて持つ仕組みです。前の記事ではpointAとpointBを別々の変数にしていました。この方法だと、地点を増やすたびに変数を増やす必要があります。配列にすれば、地点が何個でも、コードを変えずに対応できます。

配列の中身は、points[0]、points[1]のように番号を使って取り出します。番号は0から始まることに注意してください。4つある場合、番号は0、1、2、3です。

② インデックス:currentIndex

配列の「何番目の地点を目指しているか」を表す、番号です。この番号をインデックスと呼びます。

Transform target = points[currentIndex];

インデックスを使って、現在の目的地を取り出しています。インデックスを変えるだけで、目的地を切り替えられます。

③ 次の地点へ進む

currentIndex++;

インデックスを1増やす処理です。到着したときに実行することで、次の地点を目指すようになります。

if (currentIndex >= points.Length)
{
    currentIndex = 0;
}

最後の地点に到着したあと、インデックスが配列の長さ以上になったら、0に戻します。これにより、最後の地点の次に最初の地点へ戻り、ループします。このチェックがないと、配列の範囲外を参照してエラーになります。

④ 安全のためのチェック

if (points == null || points.Length == 0)
    return;

地点が1つも登録されていないときは、何もせずに処理を終えます。さらに、target == nullのチェックで、登録欄が空(None)の場合も防いでいます。

補足:余りを使った書き方

次の書き方でも、同じ動きになります。

currentIndex = (currentIndex + 1) % points.Length;

%は余りを求める演算子です。最後の番号に1を足して配列の長さと同じになると、余りが0になり、最初の地点へ戻ります。1行で書けて便利ですが、初めのうちは、本文のif文の方が読みやすいでしょう。

5. よくあるつまずき

症状確認すること
何も動かないPointsのSizeが0になっていないか
途中で止まるElementのどれかがNone(未登録)になっていないか
エラー「IndexOutOfRangeException」ゲームの再生中にSizeを減らさない。減らすときは、再生を止めてから変更する
巡回の順番が想定と違うElementの登録順を確かめる
画面外へ出てしまう各地点と移動するオブジェクトのZ座標がそろっているか

6. 練習問題

  1. 地点を5つに増やして、コードを変えずに巡回できることを確かめてみましょう
  2. 巡回地点をランダムに選ぶようにしてみましょう

ヒント(2つ目)

  • Random.Range(0, points.Length)で、ランダムなインデックスを選べます。
  • 整数を使うときのRandom.Rangeは、後ろの数(第2引数)を含みません。
  • 同じ地点を連続で選んでしまうと、動かなく見える点に注意しましょう。

解答例:ランダムに巡回する(まず自分で試してから見ましょう)

到着したところの処理を、次のように変えます。

if (Vector3.Distance(
    transform.position,
    target.position) < 0.01f)
{
    if (points.Length > 1)
    {
        int next = currentIndex;

        // 今と同じ地点にならないように、選び直す
        while (next == currentIndex)
        {
            next = Random.Range(0, points.Length);
        }

        currentIndex = next;
    }
}

地点が1つしかないときは、whileが終わらなくなるため、points.Length > 1のチェックを入れています。

7. まとめ

  • 配列で、複数の地点をまとめて扱える
  • インデックスで、目的地を切り替えられる
  • インデックスが最後を超えたら、0に戻してループさせる

次の記事では、プレイヤーを追いかける敵の移動を作ります。

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

広告