Unity 6の2Dで2つの地点を往復する移動を作る(MoveTowardsと条件分岐)
この記事は「Unity 6 2D移動の総合ガイド」の一部です。前の記事では、指定した1つの地点へ移動しました。今回は、2つの地点を行ったり来たりする移動を作ります。横スクロールゲームの敵や、動く足場によく使われる動きです。
この記事で分かること
- 目的地を切り替えて、往復する仕組み
- 到着したかどうかを、
Vector3.Distance()で判定する方法 - 条件演算子(
? :)の使い方
1. 完成する動き
オブジェクトが、PointAとPointBの間を、自動で行ったり来たりします。
2. 準備
共通の準備は総合ガイドの「共通の準備」をご覧ください。前の記事のTargetMoveが付いている場合は、取り外してください。
① 2つの地点を作る
Hierarchyで右クリックしてCreate Emptyを選び、空のGameObjectを2つ作ります。
| 名前 | Positionの例 |
|---|---|
| PointA | (-4, 0, 0) |
| PointB | (4, 0, 0) |
Z座標はどちらも0にしてください。前の記事の「注意点:Z座標をそろえる」と同じ理由です。
② 移動するオブジェクトを用意する
Squareを使い、PositionをPointAと同じ位置(-4, 0, 0)にしておきます。この記事もTransformを直接動かす方法なので、Rigidbody 2Dは不要です。
3. スクリプトを作る
PingPongMoveという名前でスクリプトを作り、移動するオブジェクトに取り付けます。
PingPongMove.cs
using UnityEngine;
public class PingPongMove : MonoBehaviour
{
[SerializeField] Transform pointA;
[SerializeField] Transform pointB;
[SerializeField] float speed = 3f;
Transform target;
void Start()
{
target = pointB;
}
void Update()
{
if (pointA == null || pointB == null)
return;
transform.position = Vector3.MoveTowards(
transform.position,
target.position,
speed * Time.deltaTime
);
if (Vector3.Distance(
transform.position,
target.position) < 0.01f)
{
target = target == pointA
? pointB
: pointA;
}
}
}
InspectorのPoint AとPoint Bに、HierarchyのPointAとPointBをそれぞれドラッグして登録します。ゲームを再生すると、往復を始めます。
4. コードの解説
① 目的地を変数で持つ
Transform target;
現在の目的地を表す変数です。Startで最初の目的地をPointBに設定しています。前の記事では目的地が1つでしたが、今回はこの変数の中身を入れ替えることで、目的地を変えます。
② 到着したかどうかを判定する
if (Vector3.Distance(
transform.position,
target.position) < 0.01f)
Vector3.Distance()は、2点間の距離を計算します。距離が0.01未満なら、「到着した」とみなします。座標が完全に一致するかを調べるのではなく、「ほぼ同じ位置」を基準にするのが安全です。
③ 目的地を切り替える(条件演算子)
target = target == pointA ? pointB : pointA;
これは条件演算子で、次のif文と同じ意味です。
if (target == pointA)
{
target = pointB;
}
else
{
target = pointA;
}
「目的地がPointAならPointBに、そうでなければPointAに切り替える」と読みます。最初は読みやすいif文でも構いません。慣れると、条件演算子は短く書けて便利です。
④ 動作の流れ
- 最初は、PointBに向かう
- PointBに到着すると、目的地をPointAに切り替える
- PointAに到着すると、目的地をPointBに切り替える
- これを繰り返す
目的地を切り替えるだけで、同じコードを使って往復できる点がポイントです。
5. よくあるつまずき
| 症状 | 確認すること |
|---|---|
| 何も動かない | Point AとPoint Bの両方を登録しているか |
| 片道だけで止まる | 到着判定の距離(0.01)を極端に小さくしていないか |
| 画面外へ出てしまう | PointA・PointB・移動するオブジェクトのZ座標がそろっているか |
| 落ちてしまう | Rigidbody 2Dが残っていないか |
6. 練習問題
- 端に到着したら、1秒停止してから折り返すように変更してみましょう
- 移動する向きにあわせて、スプライトの左右を反転させてみましょう
ヒント(1つ目)
- 「待ち時間」を表す変数を用意して、毎フレーム
Time.deltaTimeずつ減らします。 - 0より大きい間は、移動せずに処理を終えます。
ヒント(2つ目)
- スプライトの左右反転は、
SpriteRendererのflipXでできます。 - 目的地のX座標と、現在のX座標を比べると、向きが分かります。
解答例:1秒停止する(まず自分で試してから見ましょう)
[SerializeField] float waitTime = 1f;
float waitTimer;
void Update()
{
if (pointA == null || pointB == null)
return;
// 待ち時間が残っている間は、動かない
if (waitTimer > 0f)
{
waitTimer -= Time.deltaTime;
return;
}
transform.position = Vector3.MoveTowards(
transform.position,
target.position,
speed * Time.deltaTime
);
if (Vector3.Distance(
transform.position,
target.position) < 0.01f)
{
target = target == pointA
? pointB
: pointA;
waitTimer = waitTime;
}
}
到着したときにwaitTimerに1秒をセットし、次のフレームからは、時間が経つまで動かないようにしています。このように、「状態」を表す変数を使う考え方は、最後の総合演習(敵AI)でも役立ちます。
7. 発展:動く足場に使うときの注意
この移動を足場に使うと、上に乗ったプレイヤーが足場に連れて行かれないことがあります。この問題への対応は、「動く足場への追従」という発展テーマとして、あとで学びましょう。
8. まとめ
- 目的地を変数にして、入れ替えることで往復できる
Vector3.Distance()で、到着したかどうかを判定する- 条件演算子は、簡単なif文を短く書ける
次の記事では、2地点ではなく、複数の地点を順番に巡回します。










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