Unity 6の2Dでマウスでクリックした場所へ移動する(Mouse.currentとScreenToWorldPoint)

広告

この記事は「Unity 6 2D移動の総合ガイド」の一部です。前の記事では、プレイヤーを追いかける敵を作りました。今回は、マウスでクリックした場所へキャラクターが移動する、シミュレーションゲームや見下ろし型RPGで使われる移動を作ります。

この記事で分かること

  • Input SystemのMouse.currentで、マウスの状態を読み取る方法
  • 画面上の座標を、ゲーム世界の座標に変換する方法(ScreenToWorldPoint)
  • 「目的地を持っているか」を表す変数の使い方

1. 完成する動き

マウスの左ボタンで画面をクリックすると、オブジェクトがその場所へ向かって移動し、到着すると停止します。移動中に別の場所をクリックすると、目的地が変わります。

クリックした位置へ向かって、四角いオブジェクトが移動する動き

2. 前提と準備

共通の準備は総合ガイドの「共通の準備」をご覧ください。この記事では、特に次の点を確認してください。

  1. Input Systemの設定(Active Input Handling が「Input System Package (New)」または「Both」)
  2. Main CameraのタグがMainCameraになっている(初期設定のままであれば問題ありません)
  3. Main CameraのPositionが(0, 0, -10)など、Z方向を向いた標準的な2Dシーンになっている
  4. 移動するオブジェクトのZ座標が0になっている

移動するオブジェクトは、Squareを使い、Positionを(0, 0, 0)にしておきます。この記事もTransformを直接動かす方法なので、Rigidbody 2Dは不要です。他の移動スクリプトが付いている場合は、取り外してください。

3. スクリプトを作る

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

ClickMove.cs

using UnityEngine;
using UnityEngine.InputSystem;

public class ClickMove : MonoBehaviour
{
    [SerializeField] float speed = 4f;

    Camera mainCamera;
    Vector2 targetPosition;

    bool hasTarget;

    void Awake()
    {
        mainCamera = Camera.main;
    }

    void Update()
    {
        if (mainCamera == null ||
            Mouse.current == null)
            return;

        if (Mouse.current.leftButton.wasPressedThisFrame)
        {
            Vector2 mousePosition =
                Mouse.current.position.ReadValue();

            Vector3 screenPosition = new Vector3(
                mousePosition.x,
                mousePosition.y,
                -mainCamera.transform.position.z
            );

            Vector3 worldPosition =
                mainCamera.ScreenToWorldPoint(screenPosition);

            targetPosition = worldPosition;

            hasTarget = true;
        }

        if (!hasTarget)
            return;

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

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

        if (Vector2.Distance(
            transform.position,
            targetPosition) < 0.01f)
        {
            hasTarget = false;
        }
    }
}

ゲームを再生して、Gameビューをクリックしてみましょう。クリックした場所へ、オブジェクトが移動します。

4. コードの解説

① マウスを読み取る:Mouse.current

キーボードをKeyboard.currentで読み取ったのと同じように、マウスはMouse.currentで読み取ります。マウスが接続されていないときはnullになるため、先にnullチェックをしています。

コード意味
Mouse.current.leftButton.wasPressedThisFrame左ボタンを押したフレームだけtrue
Mouse.current.position.ReadValue()マウスの画面上の位置(ピクセル単位)

クリックは、押した瞬間に1回だけ反応してほしいため、isPressedではなくwasPressedThisFrameを使います。

② 画面の座標と、ゲーム世界の座標

マウスの位置は、画面上の座標(左下が(0, 0)で、ピクセル単位)です。一方、オブジェクトを動かすのはゲーム世界の座標(ユニット単位)です。この2つは別物なので、変換が必要です。

mainCamera.ScreenToWorldPoint(screenPosition)

このメソッドが、画面上の座標を、ゲーム世界の座標に変換します。

③ Zに-mainCamera.transform.position.zを指定する理由

Vector3 screenPosition = new Vector3(
    mousePosition.x,
    mousePosition.y,
    -mainCamera.transform.position.z
);

ScreenToWorldPointには、「カメラからどれだけ先の位置か」を表すZの値も渡します。カメラが(0, 0, -10)にある場合、ゲーム世界のZ=0の平面は、カメラから10先にあります。そのため、-(-10)で、10を指定しています。

これにより、クリックした位置の、Z=0平面上の座標を取得できます。

ここでは、カメラがZ方向を向いた標準的な2Dシーンで、移動対象がZ=0平面にあることを前提としています。カメラを回転させたり、移動する平面を変えたりする場合は、別の座標計算が必要です。

④ 目的地を持っているか:hasTarget

hasTargetは、「目的地があるかどうか」を表す変数です。最初はfalseです。クリックするとtrueになり、到着するとfalseに戻ります。if (!hasTarget) return;と書くことで、目的地がないときは、移動の処理をスキップします。

⑤ 移動の処理

移動は、前の記事で使ったVector2.MoveTowards()と、Z座標をそのまま残す書き方です。到着は、Vector2.Distance()で判定しています。

5. よくあるつまずき

症状確認すること
クリックしても動かないGameビューをクリックしているか。Active Input Handlingの設定
NullReferenceExceptionMain CameraのタグがMainCameraか
クリックと違う場所へ動くMain CameraのZ座標。オブジェクトのZ座標が0か
画面外へ出ていくカメラを回転させていないか
勝手に動く他の移動スクリプトが付いたままになっていないか

6. 練習問題

  1. speedを変更して、動きの変化を確かめてみましょう
  2. クリックした場所に「目印(マーカー)」を表示して、到着したら消すようにしてみましょう

ヒント(2つ目)

  • 目印には、小さなSquareやCircleを用意しておき、最初は非表示(Inspectorでチェックを外す)にします。
  • オブジェクトの表示・非表示は、SetActive(true)とSetActive(false)で切り替えます。

解答例:マーカーを表示する(まず自分で試してから見ましょう)

フィールドに次を追加し、Inspectorでマーカーを登録します。

[SerializeField] Transform marker;

クリックしたところ(hasTarget = true;の行の後)に、次を追加します。

if (marker != null)
{
    marker.position = new Vector3(
        targetPosition.x,
        targetPosition.y,
        0f
    );
    marker.gameObject.SetActive(true);
}

到着したところ(hasTarget = false;の行の後)に、次を追加します。

if (marker != null)
{
    marker.gameObject.SetActive(false);
}

7. まとめ

  • Mouse.currentで、マウスのボタンや位置を読み取れる
  • マウスの位置は画面座標なので、ScreenToWorldPointでゲーム世界の座標へ変換する
  • Zには、カメラからZ=0平面までの距離を指定する
  • 目的地の有無を、hasTargetで管理する

次の記事では、力を加えて動かすAddForceを使います。

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

広告