子对象相对父对象旋转异常:Unity环绕相机视角问题排查
问题分析与修复方案
核心问题拆解
- 旋转轴与层级逻辑混乱:相机作为球体子对象,原代码错误修改Z轴实现俯仰,且父物体旋转会干扰子对象局部旋转;同时
else return导致进入贴地模式后,球体旋转更新、碰撞检测全停,状态逻辑断裂。 - 平滑变量冲突:共用
_smoothVelocity导致球体旋转和相机旋转的平滑效果互相干扰,出现异常抖动。 - 边界控制缺失:贴地模式下相机俯仰没有限制,且未处理退出模式时的旋转重置,导致视角跳变。
修复后的完整脚本
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
相关产品推荐
相关产品推荐

