Cinemachine虚拟相机设置与自定义脚本冲突问题求助
问题描述
我将Cinemachine Virtual Camera挂载在玩家对象上,通过Inspector面板设置实现了相机跟随模型但不旋转的效果,这正是我需要的。但添加以下脚本后,原有设置被覆盖,相机开始随玩家旋转,我尝试修改代码但未成功,希望实现脚本不覆盖原有设置的效果,同时保留缩放功能。
添加的脚本代码:
using Cinemachine; using UnityEngine; public class OrbitalTest : CinemachineExtension { private const float RotateSpeed = 2f; private Vector2 _currentRotate; private Vector3 _distanceVector; private float _currentDistance = 3f; private Transform _lookAt; private CinemachineVirtualCamera _virtualCamera; private void Update() { HandleInput(); // Rotate the camera var centerMatrix = Matrix4x4.Rotate(Quaternion.Euler(_currentRotate.y, _currentRotate.x, 0f)); _distanceVector.z = -_currentDistance; var pos = centerMatrix.MultiplyPoint3x4(_distanceVector); // Update camera position and orientation _virtualCamera.transform.position = _lookAt.position + pos; _virtualCamera.transform.LookAt(_lookAt.position); } private void HandleInput() { if (Input.GetKey(KeyCode.LeftArrow)) Rotate(new Vector2(1f, 0f)); if (Input.GetKey(KeyCode.RightArrow)) Rotate(new Vector2(-1f, 0f)); if (Input.GetKey(KeyCode.UpArrow)) Rotate(new Vector2(0f, 1f)); if (Input.GetKey(KeyCode.DownArrow)) Rotate(new Vector2(0f, -1f)); if (Input.GetKey(KeyCode.W)) Scale(-0.1f); if (Input.GetKey(KeyCode.S)) Scale(0.1f); } private void Rotate(Vector2 diffDelta) { diffDelta *= RotateSpeed * Time.deltaTime * 30; _currentRotate.x += diffDelta.y; _currentRotate.y += diffDelta.x; } protected override void ConnectToVcam(bool connect) { base.ConnectToVcam(connect); if (!connect) return; _lookAt = GameObject.Find("LookAt")?.transform; if (_lookAt == null) { _lookAt = new GameObject("LookAt").transform; _lookAt.position = new Vector3(0f, 0.5f, 0f); } _virtualCamera = VirtualCamera as CinemachineVirtualCamera; _virtualCamera.LookAt = _lookAt; } protected override void PostPipelineStageCallback(CinemachineVirtualCameraBase vcam, CinemachineCore.Stage stage, ref CameraState state, float deltaTime) { var (pos, rotate) = GetTransform(_virtualCamera.LookAt.localToWorldMatrix); state.RawPosition = pos; state.RawOrientation = rotate; // Look at the player's position _virtualCamera.transform.LookAt(_lookAt.position); } private (Vector3 pos, Quaternion rotate) GetTransform(Matrix4x4 baseCenterMatrix) { var centerMatrix = baseCenterMatrix * Matrix4x4.Rotate(Quaternion.AngleAxis(_currentRotate.y, Vector3.up) * Quaternion.AngleAxis(_currentRotate.x, Vector3.right)); _distanceVector.z = -_currentDistance; var pos = centerMatrix.MultiplyPoint3x4(_distanceVector); return (pos, centerMatrix.rotation); } void Scale(float delta) { _currentDistance += delta; } }
更新:经排查,以下代码是导致旋转问题的根源,但移除这段代码后,缩放功能无法正常工作:
protected override void PostPipelineStageCallback(CinemachineVirtualCameraBase vcam, CinemachineCore.Stage stage, ref CameraState state, float deltaTime) { var (pos, rotate) = GetTransform(_lookAt.localToWorldMatrix); state.RawPosition = pos; state.RawOrientation = rotate; }
解决方案
核心思路是只让脚本处理手动旋转、缩放逻辑,完全保留相机原有跟随但不旋转的设置,具体修改如下:
1. 移除Update中直接修改相机位置/旋转的代码
原Update方法里直接修改_virtualCamera.transform.position和LookAt会干扰Cinemachine的原有逻辑,删掉这部分,改为通过Cinemachine的回调来调整位置,同时不修改旋转。
2. 修改PostPipelineStageCallback回调
只调整相机的位置以实现缩放和手动旋转,不覆盖原有旋转状态,保留Inspector设置的“不随玩家旋转”效果:
protected override void PostPipelineStageCallback(CinemachineVirtualCameraBase vcam, CinemachineCore.Stage stage, ref CameraState state, float deltaTime) { // 只在Body阶段之后调整位置,避免干扰其他阶段 if (stage != CinemachineCore.Stage.Body) return; // 计算基于LookAt的偏移位置(包含手动旋转和缩放) var rotation = Quaternion.AngleAxis(_currentRotate.y, Vector3.up) * Quaternion.AngleAxis(_currentRotate.x, Vector3.right); var offset = rotation * new Vector3(0, 0, -_currentDistance); // 只修改位置,保留原有旋转状态 state.RawPosition = _lookAt.position + offset; }
3. 调整ConnectToVcam逻辑
不要强制覆盖_virtualCamera.LookAt,而是优先使用原有设置,避免破坏Inspector配置:
protected override void ConnectToVcam(bool connect) { base.ConnectToVcam(connect); if (!connect) return; _virtualCamera = VirtualCamera as CinemachineVirtualCamera; // 优先使用原有LookAt设置,没有再创建 _lookAt = _virtualCamera.LookAt; if (_lookAt == null) { _lookAt = new GameObject("LookAt").transform; _lookAt.position = new Vector3(0f, 0.5f, 0f); _virtualCamera.LookAt = _lookAt; } }
4. 简化Update方法
只保留输入处理,移除直接操作相机的代码:
private void Update() { HandleInput(); }
完整修改后的脚本
using Cinemachine; using UnityEngine; public class OrbitalTest : CinemachineExtension { private const float RotateSpeed = 2f; private Vector2 _currentRotate; private float _currentDistance = 3f; private Transform _lookAt; private CinemachineVirtualCamera _virtualCamera; private void Update() { HandleInput(); } private void HandleInput() { if (Input.GetKey(KeyCode.LeftArrow)) Rotate(new Vector2(1f, 0f)); if (Input.GetKey(KeyCode.RightArrow)) Rotate(new Vector2(-1f, 0f)); if (Input.GetKey(KeyCode.UpArrow)) Rotate(new Vector2(0f, 1f)); if (Input.GetKey(KeyCode.DownArrow)) Rotate(new Vector2(0f, -1f)); if (Input.GetKey(KeyCode.W)) Scale(-0.1f); if (Input.GetKey(KeyCode.S)) Scale(0.1f); } private void Rotate(Vector2 diffDelta) { diffDelta *= RotateSpeed * Time.deltaTime * 30; _currentRotate.x += diffDelta.y; _currentRotate.y += diffDelta.x; } protected override void ConnectToVcam(bool connect) { base.ConnectToVcam(connect); if (!connect) return; _virtualCamera = VirtualCamera as CinemachineVirtualCamera; _lookAt = _virtualCamera.LookAt; if (_lookAt == null) { _lookAt = new GameObject("LookAt").transform; _lookAt.position = new Vector3(0f, 0.5f, 0f); _virtualCamera.LookAt = _lookAt; } } protected override void PostPipelineStageCallback(CinemachineVirtualCameraBase vcam, CinemachineCore.Stage stage, ref CameraState state, float deltaTime) { if (stage != CinemachineCore.Stage.Body) return; var rotation = Quaternion.AngleAxis(_currentRotate.y, Vector3.up) * Quaternion.AngleAxis(_currentRotate.x, Vector3.right); var offset = rotation * new Vector3(0, 0, -_currentDistance); state.RawPosition = _lookAt.position + offset; } void Scale(float delta) { // 限制最小距离,避免相机穿模 _currentDistance = Mathf.Clamp(_currentDistance + delta, 1f, 10f); } }
原理说明
- 只在Cinemachine的
Body阶段后调整位置,既实现了手动旋转和缩放,又完全保留了原有相机的旋转设置(不随玩家旋转)。 - 不再强制覆盖
LookAt,优先使用Inspector的配置,避免破坏原有设置。 - 移除了Update中直接操作相机Transform的代码,让Cinemachine的管线处理旋转逻辑,保证原有设置生效。
内容的提问来源于stack exchange,提问作者Paul Edward Pagente
相关产品推荐
相关产品推荐

