You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity第三人称肩视角相机移动实现方案技术咨询

相机控制器开发求助

我在开发Unity相机控制器时进度缓慢,没法构思出可行的实现方案。目前已经通过新输入系统获取了鼠标输入并传入lookInput变量,还定义了mouseDelta但没用到,以下是修改后的代码,求技术帮助:

这是我在评论中提到的修改版本。

using UnityEngine.InputSystem;
using UnityEngine;

public class CameraController : MonoBehaviour
{
    public InputActionAsset cameraInputs;
    private InputActionMap cameraMap;
    private InputAction lookAction;

    public float sensitivity = 10f;

    public Transform player;

    private Vector2 lookInput;

    public Vector3 defaultCameraPosition;
    private Vector3 initialOffset;
    private void Awake()
    {
        cameraMap = cameraInputs.FindActionMap("CameraMap");
        lookAction = cameraMap.FindAction("Look");
        lookAction.performed += OnLookPerformed;
    }

    private void Start()
    {
        transform.position = defaultCameraPosition;

        if (player == null)
        {
            initialOffset = transform.position - player.position;

            transform.LookAt(player.position);
        }

        Cursor.lockState = CursorLockMode.Locked;
        Cursor.visible = false;
    }

    private void Update()
    {
        Vector3 mouseDelta = new Vector3(-lookInput.y, lookInput.x, 0f) * sensitivity * Time.deltaTime;
    }

    private void LateUpdate()
    {
        RotateCamera();
    }

    private void OnEnable()
    {
        cameraMap.Enable();
    }

    private void OnDisable()
    {
        cameraMap.Disable();
    }

    private void OnLookPerformed(InputAction.CallbackContext context)
    {
        lookInput = context.ReadValue<Vector2>();
    }

    private void RotateCamera()
    {
        float yaw = lookInput.x * sensitivity * Time.deltaTime;
        float pitch = -lookInput.y * sensitivity * Time.deltaTime;

        player.Rotate(0f, yaw, 0f, Space.World);

        Vector3 currentRotation = player.transform.localEulerAngles;
        float newPitch = currentRotation.x + pitch;

        float maxPitch = 80f;
        newPitch = Mathf.Clamp(newPitch, -maxPitch, maxPitch);

        player.transform.localEulerAngles = new Vector3(newPitch, currentRotation.y, currentRotation.z);

        transform.LookAt(player.position);
    }
}

问题分析与修复建议

  1. 空指针判断逻辑错误
    Start方法里的if (player == null)写反了,应该是if (player != null),否则当player为空时执行player.position会触发空引用报错。

  2. 相机旋转逻辑错误
    当前代码把俯仰(上下拖动)和偏航(左右拖动)都应用在player上,会导致玩家模型跟着抬头低头,不符合常规第三人称相机逻辑。正确逻辑是:

    • 偏航(左右拖动)旋转player
    • 俯仰(上下拖动)旋转相机本身,同时限制俯仰角度
  3. initialOffset未被使用
    应该在LateUpdate中通过这个变量维持相机与player的相对位置,避免相机位置漂移。

  4. mouseDelta变量冗余
    当前Update里定义了mouseDelta但未使用,可直接删除,或在RotateCamera中复用该计算逻辑。

修复后的代码示例

using UnityEngine.InputSystem;
using UnityEngine;

public class CameraController : MonoBehaviour
{
    public InputActionAsset cameraInputs;
    private InputActionMap cameraMap;
    private InputAction lookAction;

    public float sensitivity = 10f;
    public Transform player;

    private Vector2 lookInput;
    private Vector3 initialOffset;
    // 单独存储俯仰角度,避免欧拉角万向锁和角度范围异常
    private float currentPitch = 0f;
    public float maxPitch = 80f;

    private void Awake()
    {
        cameraMap = cameraInputs.FindActionMap("CameraMap");
        lookAction = cameraMap.FindAction("Look");
        lookAction.performed += OnLookPerformed;
    }

    private void Start()
    {
        Cursor.lockState = CursorLockMode.Locked;
        Cursor.visible = false;

        if (player != null)
        {
            // 记录相机与玩家的初始相对偏移
            initialOffset = transform.position - player.position;
            // 初始化俯仰角度并修正欧拉角0-360度范围问题
            currentPitch = transform.localEulerAngles.x;
            if (currentPitch > 180f) currentPitch -= 360f;
        }
    }

    private void LateUpdate()
    {
        if (player == null) return;
        
        // 处理偏航:旋转玩家
        float yaw = lookInput.x * sensitivity * Time.deltaTime;
        player.Rotate(Vector3.up, yaw, Space.World);

        // 处理俯仰:旋转相机并限制角度
        currentPitch += -lookInput.y * sensitivity * Time.deltaTime;
        currentPitch = Mathf.Clamp(currentPitch, -maxPitch, maxPitch);

        // 更新相机位置和旋转
        Quaternion playerYawRotation = Quaternion.Euler(0, player.eulerAngles.y, 0);
        transform.position = player.position + playerYawRotation * initialOffset;
        transform.rotation = playerYawRotation * Quaternion.Euler(currentPitch, 0, 0);
    }

    private void OnEnable()
    {
        cameraMap.Enable();
    }

    private void OnDisable()
    {
        cameraMap.Disable();
    }

    private void OnLookPerformed(InputAction.CallbackContext context)
    {
        lookInput = context.ReadValue<Vector2>();
    }
}

关键优化说明

  • 分离玩家旋转(偏航)和相机旋转(俯仰),符合常规第三人称视角逻辑
  • 使用独立变量存储俯仰角度,避免欧拉角的万向锁问题和角度范围异常
  • 通过玩家旋转更新相机位置,维持初始相对距离和方向
  • 修复空指针判断逻辑,避免运行时错误

内容的提问来源于stack exchange,提问作者user20658958

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 20:13:23