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

子对象相对父对象旋转异常:Unity环绕相机视角问题排查

问题分析与修复方案

核心问题拆解

  1. 旋转轴与层级逻辑混乱:相机作为球体子对象,原代码错误修改Z轴实现俯仰,且父物体旋转会干扰子对象局部旋转;同时else return导致进入贴地模式后,球体旋转更新、碰撞检测全停,状态逻辑断裂。
  2. 平滑变量冲突:共用_smoothVelocity导致球体旋转和相机旋转的平滑效果互相干扰,出现异常抖动。
  3. 边界控制缺失:贴地模式下相机俯仰没有限制,且未处理退出模式时的旋转重置,导致视角跳变。

修复后的完整脚本

using UnityEngine;

public class CameraMovement : MonoBehaviour
{
    [SerializeField] private Transform target;
    [SerializeField] private Vector3 targetOffset;

    private Transform sphereRoot;
    [SerializeField] private float defaultRadius;
    private float currentRadius;
    [SerializeField] private float unclipDistance;
    [SerializeField] private LayerMask obstacleMask;

    [SerializeField] private float mouseSensitivity = 2f;

    // 球体环绕旋转参数
    private float sphereRotY;
    private float sphereRotX;
    private Vector3 currentSphereRotation;
    private Vector3 sphereSmoothVelocity;

    // 相机自身俯仰参数(仅贴地模式生效)
    private float cameraPitch;
    [SerializeField] private float maxCameraPitch = 80f;
    private Vector3 currentCameraRotation;
    private Vector3 cameraSmoothVelocity;

    [SerializeField] private Vector2 minSphereRotation = new Vector2(-0.3f, -360f);
    [SerializeField] private Vector2 maxSphereRotation = new Vector2(80f, 360f);
    [SerializeField] private float smoothTime = 0.1f;

    void Start()
    {
        sphereRoot = transform.parent;
        currentRadius = defaultRadius;
        sphereRoot.localScale = Vector3.one * defaultRadius * 2;

        cameraPitch = 0f;
        currentCameraRotation = transform.localEulerAngles;
    }
    
    void Update()
    {
        sphereRoot.position = target.position + targetOffset;

        float mouseX = Input.GetAxis("Mouse X") * mouseSensitivity;
        float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity;

        bool isInGroundMode = sphereRotX <= minSphereRotation.x;

        if (isInGroundMode)
        {
            UpdateGroundModeRotation(mouseY);
        }
        else
        {
            UpdateNormalModeRotation(mouseX, mouseY);
            ResetCameraPitch();
        }

        CheckCollision();
    }

    void UpdateNormalModeRotation(float mouseX, float mouseY)
    {
        sphereRotY += mouseX;
        sphereRotX -= mouseY;
        sphereRotX = Mathf.Clamp(sphereRotX, minSphereRotation.x, maxSphereRotation.x);

        Vector3 targetRotation = new Vector3(sphereRotX, sphereRotY, 0f);
        currentSphereRotation = Vector3.SmoothDamp(currentSphereRotation, targetRotation, ref sphereSmoothVelocity, smoothTime);
        sphereRoot.localEulerAngles = currentSphereRotation;
    }

    void UpdateGroundModeRotation(float mouseY)
    {
        cameraPitch += mouseY;
        cameraPitch = Mathf.Clamp(cameraPitch, 0f, maxCameraPitch);

        Vector3 targetRotation = new Vector3(cameraPitch, 0f, 0f);
        currentCameraRotation = Vector3.SmoothDamp(currentCameraRotation, targetRotation, ref cameraSmoothVelocity, smoothTime);
        transform.localEulerAngles = currentCameraRotation;
    }

    void ResetCameraPitch()
    {
        if (cameraPitch != 0f)
        {
            cameraPitch = Mathf.MoveTowards(cameraPitch, 0f, Time.deltaTime * 5f);
            transform.localEulerAngles = new Vector3(cameraPitch, 0f, 0f);
        }
    }

    void CheckCollision()
    {
        Vector3 rayDirection = (transform.position - sphereRoot.position).normalized;
        float maxDistance = defaultRadius;

        if (Physics.SphereCast(sphereRoot.position, 0.5f, rayDirection, out RaycastHit hit, maxDistance, obstacleMask))
        {
            currentRadius = Vector3.Distance(hit.point, sphereRoot.position) - unclipDistance;
            currentRadius = Mathf.Max(currentRadius, 0.5f);
        }
        else
        {
            currentRadius = defaultRadius;
        }

        sphereRoot.localScale = Vector3.one * currentRadius * 2;
    }
}

关键修改说明

  • 分离旋转状态:球体和相机的旋转变量、平滑变量彻底分开,避免互相干扰。
  • 修正旋转轴逻辑:贴地模式下修改相机局部X轴实现俯仰(符合Unity局部空间规则),不再错误操作Z轴。
  • 修复状态切换:移除原脚本的else return,确保两种模式下都执行碰撞检测,状态切换更稳定。
  • 添加边界控制:给相机俯仰设置最大角度,同时限制最小半径防止穿模;退出贴地模式时自动重置相机俯仰,避免视角残留。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 08:17:45