本文へスキップ
🎮

🌈 Unity ハンズオン · 入力と移動

Unityでクリックした場所に3Dモデルを移動させる方法

Raycast で地面の位置をゲット! 向きをなめらかに変えながら歩かせよう

ゴールは「クリックした床の場所にキャラが歩いてゆき、顔を進行方向に向ける」こと。 いきなり物理(Rigidbody)や経路AI(NavMesh)は使わず、Transform だけで中身が見えるカンタン実装にします。

自己紹介

ココ先生(講師)・喜ぶ
ココ先生やさしい講師
このチュートリアルを一緒に進める講師です。Raycast や移動のコツを、図やたとえ話でなるべくやさしく説明していきます。
はじめちゃん(学習者)・笑う
はじめちゃんUnity習い始めたて
Unity を始めたばかりの生徒です。わからないことは遠慮なく質問するタイプ。一歩ずつついてきます。

オープニング対談

ココ先生(講師)・楽しむ
ココ先生こんにちは、ココ先生だよ。今日はマウスで床をポチッとした場所へ、3Dキャラをお散らせしてみよう!
はじめちゃん(学習者)・驚く
はじめちゃんやったー!…でも、画面は2Dなのに、どうやって「床のどこ」がわかるの?
ココ先生(講師)・喜ぶ
ココ先生いい質問! カメラから「見えない光の道〈Ray〉」を伸ばして、床に当たったドンピシャの座標をもらうの。それがポイントだよ 🎯

🎁このレッスンのおみやげ(学べること)

  • 🖱️ クリックで「どこへ行くか」を決める流れ
  • 📷 ScreenPointToRay で Ray 作成
  • 🎯 Raycast で地面の座標ゲット
  • 🚶 MoveTowards で歩かせる
  • 🔄 LookRotation Slerp で向き変更
  • 🏷️ LayerMask で床だけ判定

このページの手順は Unity 向けです。Main Camera の ScreenPointToRay Physics.Raycast が中心になります。

前提:シーンに「当たり判定のある地面」と「タグ付きメインカメラ」があること。

1👋導入 — いまからやること

このチュートリアルでは、画面上をクリックした場所に3Dモデルを移動させる処理を作ります。 キャラクターは移動先へ向かって進み、移動中は進行方向を向くようにします。

RPG、シミュレーション、クリック操作のアクションなどでよく使われる基本パターンです。 いきなり物理やAIを入れるとデバッグが難しくなるため、最初は Transform だけで成功体験をつくりましょう。

はじめちゃん(学習者)・泣く
はじめちゃん用語がたくさん出てきそうでちょっとビクビクしてます…
ココ先生(講師)・笑う
ココ先生だいじょうぶ! 「クリック → 光の道 → 床に当たる → そこへ歩く」この4拍子だけ先に覚えよう。細かい名前はあとで追いかければOK!

2🎬完成イメージ(ストーリー版)

完成後の動きは、次の通り。下のイラストとフロー図をあわせて読むとイメージがつかみやすいです。

クリックからレイキャスト、移動、向き変更までの流れを示すイラスト
ざっくりイメージ:クリック → Ray → 地面の点 → 歩く・向く
  1. プレイヤーが画面上の地面をクリックする
  2. Camera からクリック位置へ Ray(見えない直線)を飛ばす
  3. Raycast で Ray が地面に当たった位置を 移動先として保存する
  4. 3Dモデルがその位置へ移動する
  5. 移動中、3Dモデルは移動方向を向く
  6. 目的地に近づいたら停止する
画面クリックGame ビューで
Ray を発射ScreenPointToRay
床にヒット?Layer=Ground
座標GEThit.point
ちょい移動MoveTowards
向きチェンジSlerp
到着っ!stopDistance

画面幅に応じて折り返します。横スクロールは不要です。

イメージのコツ

Ray は「カメラの目からマウス先へまっすぐ伸びるレーザー」だと想像してみてください。 それが床の Collider に当たったとんがった位置が目的地になります。

補足:Ray=3D空間のまっすぐな線。レイキャスト=その線がオブジェクトに当たったかを調べる処理。

3🧰使用するUnity機能(カードでざっと)

下のカードを上から順に追うと、このレッスンの骨組みが頭に入ります。

📷
Camera.main.ScreenPointToRay

画面ピクセル → カメラから伸びる Ray を作成。

🎯
Physics.Raycast

Ray が Collider に当たったか、どの点で当たったかを判定。

🖱️
Input.GetMouseButtonDown(0)

左クリックが押された瞬間だけ反応(押しっぱなしでは連打されません)。

📍
Transform.position

キャラの「いまどこ」にあたる座標。ここを少しずつ動かして歩かせます。

🚶
Vector3.MoveTowards

毎フレームちょっとずつ目的地へ。ドリブルで運びやすい定番API。

🧭
Quaternion(クォータニオン)

くるりと回転を表す型。Euler角よりからまりにくいのが魅力。

👀
Quaternion.LookRotation

ベクトルの向きを「顔の正面(Z+)」にそろえる回転を生成。

Quaternion.Slerp

回転をなめらかにブレンド。キビキビ動きすぎ防止。

🏷️
LayerMask

Raycast が反応するレイヤーを指定。床だけに絞るのが今回のミソ。

4🧱事前準備(シーンをかためる)

まずは小さなステージを用意します。難しく考えず、床・キャラ・カメラ・太陽の4点セットです。

  • 🟫 Ground
  • 🧍 Player
  • 📷 Main Camera
  • ☀️ Directional Light
地面・プレイヤー・カメラ・ライトをそろえたシーン構成のイラスト
事前準備:床(Ground)・操作する Player・見える位置の Main Camera・ライトのイメージ
Unity で Plane を床として配置するイメージ
Step 1:Plane で床をつくり、Ground として扱うイメージ
Step 1
Ground(お庭)
  • メニューから 3D Object → Plane を作成
  • 名前を Ground
  • Mesh Collider(または Box Collider)が付いていることを確認
  • 後述の手順で Layer を Ground に設定
キャプセルなどの Player を床の上に置くイメージ
Step 2:操作する Player を床の上に配置するイメージ
Step 2
Player(あなたの分身)
  • Capsule / Cube / 任意の3Dモデルを配置し、名前を Player
  • 前後が分かりやすい形状にすると、向きの学習が楽です
  • 見た目用モデルを 子オブジェクト に分ける構成も後半で解説します
斜め上から床と Player が見えるよう Main Camera を配置するイメージ
Step 3:床と Player が見える画角に Main Camera を置くイメージ
Step 3
Main Camera(観客席)
  • 地面と Player が見える位置へ移動・回転
  • 例:Position(0, 8, -8)、Rotation(45, 0, 0)のような斜め上からの構図
  • タグが MainCamera であることを確認(Camera.main に必要)

5🏷️レイヤーと LayerMask

クリック対象を地面だけにしたいので、LayerMask を使います。 LayerMask を使うと、Raycast が「どのレイヤーの Collider にだけ反応するか」を制限できます。 壁やアイテムを誤って目的地にしない、UIと干渉しにくくする、といった理由でも重要です。

なぜ Layer? というお話

ぜんぶのオブジェクトに反応させると「空をクリックしたのに宝箱が反応した」みたいないたずらが起きやすいです。今回は Ground チームだけ参加 のクラブ活動だと思ってください。
  1. Unity 上部メニューまたは Inspector の Layer ドロップダウンから Edit Layers…
  2. 空いている User Layer に Ground を追加
  3. Ground オブジェクトの Layer を Ground に設定
  4. 後述のスクリプトで groundLayer に Ground をチェックして指定

6📝スクリプト:ClickMoveController.cs

どのオブジェクトにアタッチする?

このスクリプトは、動かしたいキャラクター(Player)が付いているゲームオブジェクトにアタッチします。 Hierarchy で事前準備で置いた Player(Capsule など)を選び、Inspector の Add Component から ClickMoveController を追加するのがわかりやすいです。

地面(Ground)Main Cameraには付けません。 transform.positiontransform.rotation を書き換えているのは「このコンポーネントが付いたオブジェクト」だからです。

コメント多めなので、行の意味を追いながらコピー&ペーストして構いません。

ClickMoveController.cs
C#
using UnityEngine;

/// <summary>
/// 画面クリックで地面(指定レイヤー)上の点へ移動し、
/// 移動中は進行方向をなめらかに向けるシンプルなコントローラです。
/// Rigidbody / NavMesh は使いません(Transform ベース)。
/// </summary>
public class ClickMoveController : MonoBehaviour
{
    [Header("移動設定")]
    [Tooltip("1秒あたりの移動距離の目安。大きいほど速く進みます。")]
    [SerializeField] private float moveSpeed = 5f;

    [Tooltip("向きが変わる速さ。大きいほど素早く目的地の方向を向きます。")]
    [SerializeField] private float rotateSpeed = 10f;

    [Tooltip("目的地との距離がこの値以下になったら「到着」とみなして止まります。")]
    [SerializeField] private float stopDistance = 0.05f;

    [Header("クリック判定")]
    [Tooltip("Raycast が反応するレイヤー。地面だけをオンにすると他のオブジェクトをクリックしても無視できます。")]
    [SerializeField] private LayerMask groundLayer;

    // 移動目標のワールド座標
    private Vector3 targetPosition;

    // 移動先が設定されているか(false ならその場に留まる)
    private bool hasTarget;

    private void Start()
    {
        // 初期位置をターゲットにしておくと、開始直後に誤って移動しにくいです
        targetPosition = transform.position;
    }

    private void Update()
    {
        HandleClick();
        MoveToTarget();
    }

    /// <summary>
    /// 左クリックがあったら、カメラから Ray を飛ばして地面を探します。
    /// </summary>
    private void HandleClick()
    {
        // 0 = 左クリック。モバイル対応は別レッスンで Touch へ置き換えます。
        if (!Input.GetMouseButtonDown(0))
        {
            return;
        }

        // マウス位置 → 3D 空間へ伸びる Ray(光線)
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

        // Ray を最大 100m 飛ばし、groundLayer だけに当たり判定
        if (Physics.Raycast(ray, out RaycastHit hit, 100f, groundLayer))
        {
            targetPosition = hit.point; // 当たった表面の座標
            hasTarget = true;
        }
    }

    /// <summary>
    /// 目的地へ少しずつ近づき、進行方向を向きます。
    /// </summary>
    private void MoveToTarget()
    {
        if (!hasTarget)
        {
            return;
        }

        Vector3 currentPosition = transform.position;

        // Y は現在の高さを維持(キャラが地面にめり込まない / 浮かないよう水平移動)
        Vector3 targetFlatPosition = new Vector3(
            targetPosition.x,
            currentPosition.y,
            targetPosition.z
        );

        Vector3 direction = targetFlatPosition - currentPosition;

        // 十分近ければ停止
        if (direction.magnitude <= stopDistance)
        {
            hasTarget = false;
            return;
        }

        // まず向きを合わせてから(または同フレーム内で)位置を進める
        RotateToDirection(direction);

        transform.position = Vector3.MoveTowards(
            currentPosition,
            targetFlatPosition,
            moveSpeed * Time.deltaTime
        );
    }

    /// <summary>
    /// direction の向きを、なめらかに LOOK させる。
    /// </summary>
    private void RotateToDirection(Vector3 direction)
    {
        // ほぼゼロベクトルなら回転しない(エラー回避)
        if (direction.sqrMagnitude <= 0.0001f)
        {
            return;
        }

        Quaternion targetRotation = Quaternion.LookRotation(direction.normalized);

        transform.rotation = Quaternion.Slerp(
            transform.rotation,
            targetRotation,
            rotateSpeed * Time.deltaTime
        );
    }
}

7🔍コード解説(ねっこから理解)

7-1. 変数の説明

  • moveSpeed:1秒あたりどれだけ進むかの目安。大きいほど速い。
  • rotateSpeed:向きが変わる速さ。Slerp の補間係数に掛け合わせます。
  • stopDistance:この距離以下なら「着いた」とみなす。小さすぎると永遠に到達しようとしてガクガクすることがあります。
  • groundLayer:Raycast が反応するレイヤー。地面だけオンにします。
  • targetPosition:移動したいワールド座標。
  • hasTarget:移動先が有効か。false のときは移動処理を省きます。

7-2. クリック位置を取得する処理

コード
C#
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

if (Physics.Raycast(ray, out RaycastHit hit, 100f, groundLayer))
{
    targetPosition = hit.point;
    hasTarget = true;
}
  • Input.mousePosition は画面ピクセル座標(左下基準)。その位置から視線を伸ばすイメージ。
  • ScreenPointToRay でその視線を Ray として表現。
  • Physics.Raycast が地面の Collider に当たれば true。
  • hit.point が「表面上の当たった地点」のワールド座標。

7-3. Y座標を固定する理由

コード
C#
Vector3 targetFlatPosition = new Vector3(
    targetPosition.x,
    currentPosition.y,
    targetPosition.z
);

地面をクリックした hit.point の Y をそのまま使うと、地面の微妙な傾きやメッシュの形状によってキャラの高さが毎回変わることがあります。キャラを水平移動だけさせたいなら、Y は現在の高さに固定して XZ 平面だけ動かすのが安全です。

7-4. 移動処理

コード
C#
transform.position = Vector3.MoveTowards(
    currentPosition,
    targetFlatPosition,
    moveSpeed * Time.deltaTime
);
  • MoveTowards は毎フレーム「最大 moveSpeed * deltaTime だけ」近づきます。
  • Time.deltaTime を掛けるとフレームレートに依存しにくく、遅いPCでも同じ速さに近づけます。

7-5. 移動方向を向く処理

コード
C#
Quaternion targetRotation = Quaternion.LookRotation(direction.normalized);

transform.rotation = Quaternion.Slerp(
    transform.rotation,
    targetRotation,
    rotateSpeed * Time.deltaTime
);
  • direction は「今いる場所 → 目的地」へのベクトル。
  • LookRotation でその方向を前に見る回転を生成。
  • Slerp で直前の回転から滑らかに補間。 rotateSpeed を上げると素早く向きます。

8⚙️Unity エディタでやることリスト

  1. 1
    Player オブジェクトを選択する
  2. 2
    Project で作成した ClickMoveController.cs をドラッグ&ドロップでアタッチ
  3. 3
    Layer で Ground を作成し、Ground オブジェクトに適用
  4. 4
    Player の Inspector で groundLayer に Ground にチェックを付ける
  5. 5
    Play を押して、Scene ビューではなく Game ビューで地面をクリック
  6. 6
    Player が移動し、向きが変われば成功

9✔️動作確認チャレンジ

全部チェックできたら、今日のミッションクリアです 🎉

  1. 1
    地面をクリックすると Player が移動する
  2. 2
    Player がクリック位置の方向を向く
  3. 3
    目的地に近づくと停止する
  4. 4
    地面以外をクリックしても反応しない
  5. 5
    Player の高さが不自然に変わらない

10🆘つまずきポイント図鑑

トラブルシュートのヒントを連想させるイラスト
つまずいたら:原因を切り分けて、下の例から近いものを読んでみてください

例1:クリックしてもピクリともしない

原因の例:Ground に Collider がない / Ground Layer が未設定 / groundLayer に Ground が含まれていない / Camera.main が null。

対処:Collider と Layer を確認。Main Camera のタグが MainCamera か確認します。

例2:キャラがユニコーンみたいに上下に飛ぶ

原因hit.point の Y をそのまま使っている。

対処:Y は現在の高さに固定した targetFlatPosition を使う(本コードの通り)。

例3:向きが横向き・逆さまに見える

原因:モデルの「前」が Unity 標準の Z+ 前方向と違う。

対処:親子構造で子モデルだけ Rotation を直す(次セクション)。

例4:カメラのほうを向いてしまう

原因:Y を含む direction で LookRotation してしまい、斜め視線になる。

対処:移動は XZ にそろえ、同じ高さ同士の方向ベクトルで回転する(本コードはその形)。

11🧭モデルが横向き! そんなときは親子ワザ

モデルによっては見た目の正面が Unity の前(Z+)と違います。 スクリプトを複雑にするより、親子構造で見た目だけ直すのが管理しやすいです。

  • PlayerRoot(ここに ClickMoveController)
  • └── Model(見た目のメッシュ。Rotation を微調整)

移動・回転の制御は親の PlayerRoot に任せ、子は見た目専用にします。

12📍+α:クリック地点にキラッとマーク

移動先が視覚的に分かると、RTS やタクティカルRPGっぽい演出になります。実装の考え方の例だけ挙げます。

  • 小さな Quad + 半透明マテリアル、または Decal、パーティクルをプレハブ化
  • Raycast が成功したら hit.point にインスタンス。古いマーカーは Destroy または再利用
  • Y を少し浮かせると Z-fighting(表面とちらつき)を防げます

13🕺+α:歩きモーションをつなぐ

Animator で Bool パラメータ(例:IsMoving)を用意し、移動中だけ歩き、停止で待機に戻すと体験が良くなります。

例(別スクリプトに分けると安全)
C#
// Animator animator; // SerializeField で参照を取る
bool moving = /* hasTarget など */;
animator.SetBool("IsMoving", moving);

実プロジェクトでは移動速度に応じて SetFloat でブレンドツリーを動かすことも多いですが、最初は Bool トグルで十分です。

15🏆今日の宝箱(まとめ)

このチュートリアルでは、Unity で画面クリックへ3Dモデルを移動させる流れを学びました。

  • ScreenPointToRay でクリック位置から Ray を作る
  • Physics.Raycast + LayerMask で地面の点を取得する
  • Vector3.MoveTowards で目的地へ近づく
  • Quaternion.LookRotation で進行方向を向く
  • Quaternion.Slerp でなめらかに回転する

🚀次のステージへ(おすすめ導線)

  • クリック位置に移動マーカーを表示する(演出)
  • クリック移動に歩きアニメーションを追加する(Animator)
  • NavMeshAgent で障害物を避けて移動する
  • マウスクリックでオブジェクトを選択する(別レイヤー・UI考慮)
  • スマホのタップ操作でキャラクターを移動する(Touch 系)

サイト内の関連:Unity ロードマップ記事一覧

COMIC MODEキャラ対談で総復習しよ!

ココ先生 🧑‍🏫 と はじめちゃん 🎒 の漫才トークで、もう一度だけおさらい。

ココ先生(講師)・楽しむ
ココ先生さて総復習! ゴールは「床をクリックしたところへ、向きを変えながら歩く」こと。コードでは HandleClick と MoveToTarget の2つが主役だよ。
はじめちゃん(学習者)・混乱
はじめちゃんクリックした場所って、どうやって3D空間の座標に変えるんですか?
ココ先生(講師)・笑う
ココ先生カメラから Ray という「見えない直線」を飛ばします。Ray が地面の Collider に当たった地点が、ワールド座標として取れます。
はじめちゃん(学習者)・驚く
はじめちゃん移動する方向を向かせるには?
ココ先生(講師)・喜ぶ
ココ先生今いる位置から目的地へのベクトルを使い、Quaternion.LookRotation でその方向を「前」にします。Slerp で急に振り向かず自然に回転できます。
はじめちゃん(学習者)・怒る
はじめちゃん地面以外をクリックしたとき動いてほしくないです。
ココ先生(講師)・寝る
ココ先生最後にもう一回だけね… zzz なんて寝ちゃダメ! 左クリック → Ray → Ground に当たる → hit.point が目的地。あとは MoveTowards で歩かせて、Slerp で顔を向ければウマくいくよ。
はじめちゃん(学習者)・笑う
はじめちゃんLayerMask 忘れたら、空気読まずに全部反応しちゃうやつだね…!
ココ先生(講師)・喜ぶ
ココ先生その通り! 床オンリーにしておけば「クリックしたのに動かない!」デバッグもしやすくなるの。えらいぞはじめちゃん 💮

🎮理解チェックミニゲーム(3問)

ボタンを押してから「正解を見る」と、ココ先生が解説してくれるよ。

1

クイズ

Q1. 画面上のクリック位置から3D空間へRayを飛ばすために使う処理はどれ?

2

クイズ

Q2. Raycastで地面に当たった位置を取得するために使う値はどれ?

3

クイズ

Q3. 移動方向を向く回転を作るために使う処理はどれ?