U
章 03镜头与操作6 课 — 点击打开
02 / 06 · 10 分钟
镜头与操作

第三人称环绕镜头

到目前为止的项目

Assets/

Scenes/

Arena.unity

CameraTarget

Scripts/

PlayerMovement.csFollowCamera.cs→OrbitCamera.cs

你已经有的这一课新增这一课替换

第二步。FollowCamera 只会从一个固定角度看着你,你永远看不到 Knight 的身后。把它从 Main Camera 上摘掉,换成这个脚本。

第三人称相机就是装在一根杆子末端上的相机。杆子的根部钉在角色身上,鼠标负责把杆子转动。

动态图解

鼠标横向移动让杆子水平旋转(yaw),纵向移动把它抬高或压低(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'
}
  • 之所以写 pitch -= mouseY,是因为在 Unity 里 pitch 为正时相机是朝下的。用减法才有那种“往前推就是抬头”的标准手感。

  • 限制 pitch 不是可选项。不加限制,相机就会翻过头顶,整个世界跟着倒过来。

  • rot * new Vector3(0, 0, -distance) 就是全部技巧:把一个朝后的向量旋转一下,得到的就是相机位置。

接到移动逻辑里

你的 PlayerMovement 本来就在读 Camera.main.transform.eulerAngles.y,所以这个相机一装上就能配合 —— 此时前进就是离这台相机越来越远。

不说谎的鼠标灵敏度

  1. 1

    Mouse X/Y 本身就是每帧的差值,所以再乘 Time.deltaTime 严格来说是错的 —— 但它能让 Inspector 里的数值在不同 FPS 下保持稳定。选定一种做法就一路用下去。

  2. 2

    把灵敏度开放到设置菜单里。每个玩家的鼠标 DPI 都不一样。

  3. 3

    再加一个 Invert Y 开关。大约五分之一的玩家一定要它,没有的话他们直接就弃坑了。

C# 脚本
if (Input.GetKeyDown(KeyCode.Escape))
{
    Cursor.lockState = CursorLockMode.None;
    Cursor.visible = true;
}

常见错误

  • 把 yaw 和 pitch 存进 transform.eulerAngles 再读回来,clamp 就失效了:Unity 会把角度换算到 0–360,于是 pitch 读出来是 350 而不是 -10。这两个角度要放在你自己的 float 字段里。

  • 为菜单加的或 Cinemachine 生成的第二台相机如果也带着 MainCamera 标签,Camera.main 就会返回它先找到的那台,前方向直接翻转。只保留一个 MainCamera 标签,或者改成读这个脚本的 Yaw。

  • obstacles 留在 Nothing,SphereCast 永远打不中,相机照样穿墙;把它设成关卡几何体所在的层。千万别把 Player 层也包进去,否则相机会躲到角色自己身后。