U
タブ 03カメラと入力6 レッスン — タップで開く
02 / 06 · 10 分
カメラと入力

三人称の旋回カメラ

ここまでのプロジェクト

Assets/

Scenes/

Arena.unity

CameraTarget

Scripts/

PlayerMovement.csFollowCamera.cs→OrbitCamera.cs

すでにあるものこのレッスンで追加このレッスンで置き換え

二段目です。FollowCamera は決まった角度から見ているだけなので、Knight の後ろ側を一度も確認できません。Main Camera から外して、代わりにこちらを付けてください。

三人称カメラは「棒の先に付いたカメラ」だと思うのが一番早いです。棒の根元はキャラクターに固定され、マウスはその棒を回す役目になります。

アニメーション図

マウスの X は棒を水平方向に回し(yaw)、マウスの Y はそれを上下に振ります(pitch)。

回すためのリグを組む

OrbitCamera.cs
using UnityEngine;

public class OrbitCamera : MonoBehaviour
{
    [SerializeField] private Transform target;          // the Knight's CameraTarget child, from tab 02
    [SerializeField] private float distance = 6f;
    [SerializeField] private float sensitivity = 240f;
    [SerializeField] private float minPitch = -25f;
    [SerializeField] private float maxPitch = 65f;
    [SerializeField] private LayerMask obstacles;

    private float yaw;
    private float pitch = 20f;

    void Start()
    {
        Cursor.lockState = CursorLockMode.Locked;       // hide and centre the cursor
        Cursor.visible = false;
    }

    void LateUpdate()
    {
        // ---- 1. accumulate the angles
        yaw   += Input.GetAxis("Mouse X") * sensitivity * Time.deltaTime;
        pitch -= Input.GetAxis("Mouse Y") * sensitivity * Time.deltaTime;
        pitch  = Mathf.Clamp(pitch, minPitch, maxPitch);   // never flip over

        // ---- 2. build the rotation, then push back along -Z
        Quaternion rot = Quaternion.Euler(pitch, yaw, 0f);
        Vector3 pivot = target.position;
        Vector3 wanted = pivot + rot * new Vector3(0f, 0f, -distance);

        // ---- 3. pull in if a wall is in the way
        float realDistance = distance;
        if (Physics.SphereCast(pivot, 0.3f, (wanted - pivot).normalized,
                               out RaycastHit hit, distance, obstacles))
            realDistance = hit.distance - 0.2f;

        transform.position = pivot + rot * new Vector3(0f, 0f, -realDistance);
        transform.rotation = rot;
    }

    public float Yaw => yaw;   // movement uses this to know 'forward'
}
  • Unity では pitch が正の値だとカメラが下を向くため、pitch -= mouseY にしています。引き算にすると、定番の「マウスを手前に押す=見上げる」という手感になります。

  • pitch をクランプするのは省略できません。上限を設けないとカメラが頭の上を通過し、世界がひっくり返ります。

  • rot * new Vector3(0, 0, -distance) がこの仕組みの核心です。後ろ向きのベクトルを回転させれば、それがそのままカメラの位置になります。

移動処理に接続する

PlayerMovement はすでに Camera.main.transform.eulerAngles.y を読んでいるので、このカメラは付けた瞬間から正しく動きます。つまり「前」は、このカメラから遠ざかる方向になります。

誤差のないマウス感度

  1. 1

    Mouse X/Y はすでにフレームごとの差分なので、Time.deltaTime を掛けるのは厳密には間違いです。ただし Inspector の数値がフレームレートに左右されずに安定するという利点もあります。流儀を一つ決めて、そこへ揃えてください。

  2. 2

    感度は設定メニューから変更できるように公開してください。プレイヤーの数だけマウスの DPI が違います。

  3. 3

    Y 軸を反転するトグルも追加しておいてください。五人に一人は必ず必要とし、それが無いままならゲームを離れてしまいます。

C# スクリプト
if (Input.GetKeyDown(KeyCode.Escape))
{
    Cursor.lockState = CursorLockMode.None;
    Cursor.visible = true;
}

よくあるミス

  • yaw と pitch を transform.eulerAngles に保存して読み戻すとクランプが壊れます。Unity は角度を 0~360 に丸めるので、pitch が -10 ではなく 350 として読めてしまうからです。二つの角度は自前の float フィールドに持ってください。

  • メニュー用や Cinemachine が作った二台目のカメラにも MainCamera タグが付いていると、Camera.main は先に見つかった方を返し、前方向が反転します。MainCamera タグは一つだけにするか、代わりにこのスクリプトの Yaw を読んでください。

  • obstacles を Nothing のままにすると SphereCast は何にも当たらず、カメラは相変わらず壁を抜けます。ステージのジオメトリのレイヤーを指定してください。Player レイヤーは絶対に含めないこと。含めるとカメラがキャラクター自身の後ろに隠れてしまいます。