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

Unity相机局部旋转异常求助:单轴丢失局部朝向

问题分析与解决方案

问题根源

  • 旋转轴错误使用了Player对象的世界空间轴向(transform.up/transform.right/transform.forward),而非相机当前的局部空间轴向,导致旋转偏离局部坐标系统。
  • 从Quaternion.identity重新构建旋转的方式,相当于每次丢弃相机当前的旋转状态,从初始值叠加,无法保持局部朝向的连续性。

修正后的代码

public class Player : MonoBehaviour
{
    [Header("Camera")]
    [SerializeField] private Camera _camera;
    [SerializeField] private Vector2 _xMinMaxRotation = new Vector2(-90, 90);
    [SerializeField] private Vector2 _yMinMaxRotation = new Vector2(-90, 90);
    [SerializeField] private Vector2 _zMinMaxRotation = new Vector2(-25, 25);
    [SerializeField] private float _mouseXSensistivity = 1f;
    [SerializeField] private float _mouseYSensistivity = 1f;
    [SerializeField] private float _mouseZSensistivity = 1f;
    [SerializeField] private float _xStartRotation = 0f;
    [SerializeField] private float _yStartRotation = 0f;
    [SerializeField] private float _zStartRotation = 0f;

    private float _currentXRot;
    private float _currentYRot;
    private float _currentZRot;

    void Start()
    {
        Cursor.lockState = CursorLockMode.Locked;
        _currentXRot = _xStartRotation;
        _currentYRot = _yStartRotation;
        _currentZRot = _zStartRotation;
        UpdateCameraRotation();
    }

    void Update()
    {
        var mouseDelta = new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y"));
        UpdateCameraRotationInput(mouseDelta);
    }

    private void UpdateCameraRotationInput(Vector2 mouseDelta)
    {
        _currentXRot += mouseDelta.x * _mouseXSensistivity * Time.deltaTime * 100f;
        _currentYRot -= mouseDelta.y * _mouseYSensistivity * Time.deltaTime * 100f;

        if (Input.GetKey(KeyCode.Q))
        {
            _currentZRot += _mouseZSensistivity * Time.deltaTime * 50f;
        }
        else if (Input.GetKey(KeyCode.E))
        {
            _currentZRot -= _mouseZSensistivity * Time.deltaTime * 50f;
        }
        else
        {
            _currentZRot = Mathf.MoveTowards(_currentZRot, 0f, 2f * _mouseZSensistivity * Time.deltaTime * 50f);
        }

        _currentXRot = Mathf.Clamp(_currentXRot, _xMinMaxRotation.x, _xMinMaxRotation.y);
        _currentYRot = Mathf.Clamp(_currentYRot, _yMinMaxRotation.x, _yMinMaxRotation.y);
        _currentZRot = Mathf.Clamp(_currentZRot, _zMinMaxRotation.x, _zMinMaxRotation.y);

        UpdateCameraRotation();
    }

    private void UpdateCameraRotation()
    {
        // 直接设置相机局部欧拉角,Unity自动转换为局部空间四元数,避免万向锁
        Quaternion rotation = Quaternion.Euler(_currentYRot, _currentXRot, _currentZRot);
        _camera.transform.localRotation = rotation;
    }
}

关键修正点

  1. 局部空间旋转:通过Quaternion.Euler设置相机的localRotation,确保旋转基于相机自身的局部坐标系统,而非Player的世界坐标。
  2. 简化Z轴逻辑:用Mathf.MoveTowards替代冗余的条件判断,实现更平滑的自动回正效果。
  3. 状态连续更新:基于当前累积的旋转值更新相机状态,保持局部朝向的连续性,而非每次从初始状态重建旋转。

可选:纯四元数增量旋转(更严谨的局部旋转)

如果希望完全通过四元数增量更新(避免欧拉角的潜在问题),可以替换UpdateCameraRotation方法:

private void UpdateCameraRotation()
{
    Quaternion currentRot = _camera.transform.localRotation;

    // 计算各轴的增量旋转(基于局部空间轴)
    Quaternion xDeltaRot = Quaternion.AngleAxis(_currentXRot - currentRot.eulerAngles.y, Vector3.up);
    Quaternion yDeltaRot = Quaternion.AngleAxis(_currentYRot - currentRot.eulerAngles.x, Vector3.right);
    Quaternion zDeltaRot = Quaternion.AngleAxis(_currentZRot - currentRot.eulerAngles.z, Vector3.forward);

    // 四元数右乘:新旋转应用在当前旋转的局部空间
    _camera.transform.localRotation = yDeltaRot * xDeltaRot * zDeltaRot * currentRot;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 02:10:34