Unity玩家同时移动旋转时Cinemachine相机画面卡顿排查
问题现象
- 挂载在玩家角色上的脚本用于实现进入/退出碰撞体时,触发玩家同时执行旋转与移动的逻辑,但玩家同时启动旋转和移动时画面出现明显卡顿,暂无法定位卡顿来源是玩家逻辑本身还是相机问题。
- 卡顿仅在玩家同时执行移动与旋转操作时出现,旋转结束后卡顿随即消失,仅在旋转与移动刚启动的短时间段内触发。
- 经测试验证:将普通Camera设为玩家子物体运行时无任何卡顿,切换回Cinemachine FreeLook相机时卡顿稳定复现。问题复现视频时序参考:视频开头使用普通相机无卡顿,1分09秒切换为Cinemachine相机,1分20秒开始出现明显卡顿。
排查参考文件
以下为挂载在玩家物体上的DistanceCheck脚本、玩家组件配置截图、Cinemachine相机配置截图,以及挂载在所有相机父物体上的CamerasControl脚本:
玩家挂载DistanceCheck脚本
using System.Collections; using System.Collections.Generic; using TMPro; using UnityEngine; using UnityEngine.UI; using UnityStandardAssets.Characters.ThirdPerson; using Cinemachine; public class DistanceCheck : MonoBehaviour { public Transform objectToRotatetowards; public float slowDownLerpDuration; public float speedUpLerpDuration; public float rotationSpeed; public GameObject descriptionTextImage; public TextMeshProUGUI text; public ThirdPersonUserControl thirdPersonUserControl; private Animator anim; private float timeElapsed = 0; private float startValue = 1; private float endValue = 0; private float valueToLerp = 0; private bool startRotating = false; private bool slowOnBack = true; private bool exited = false; private Vector3 exitPosition; private float distance; private bool rotateTowards = false; private bool rotateBackwards = false; private bool rotateTowardsObject = false; void Start() { anim = transform.GetComponent<Animator>(); } private void FixedUpdate() { if (startRotating) { if (rotateTowards) { if (rotateTowardsObject) { Vector3 targetDirection = objectToRotatetowards.position - transform.position; float singleStep = 3f * Time.deltaTime; Vector3 newDirection = Vector3.RotateTowards(transform.forward, targetDirection, singleStep, 0.0f); transform.rotation = Quaternion.LookRotation(newDirection); } if (rotateBackwards) { StartCoroutine(DoRotate(2f)); rotateBackwards = false; } } } if (exitPosition != new Vector3(0, 0, 0) && slowOnBack) { distance = Vector3.Distance(transform.position, exitPosition); } if (distance > 5 && slowOnBack) { slowOnBack = false; StartCoroutine(SlowDown()); } } private void OnTriggerExit(Collider other) { if (other.tag == "NoExit") { rotateTowardsObject = true; rotateBackwards = false; descriptionTextImage.SetActive(true); if (other.TryGetComponent(out ColliderInfo info)) { text.text = info.onExitText; rotateTowards = info.rotateTowards; } RepositionPlayer(); } else if (other.tag == "NoEntry") { OnPlayerRepositioned(); } } private void OnTriggerEnter(Collider other) { if (other.tag == "NoExit") { OnPlayerRepositioned(); } else if (other.tag == "NoEntry") { rotateTowardsObject = false; rotateBackwards = true; descriptionTextImage.SetActive(true); if (other.TryGetComponent(out ColliderInfo info)) { text.text = info.onEnterText; rotateTowards = info.rotateTowards; } RepositionPlayer(); } } private void RepositionPlayer() { exited = true; slowOnBack = true; exitPosition = transform.position; thirdPersonUserControl.enabled = false; StartCoroutine(SlowDown()); } private void OnPlayerRepositioned() { exited = false; startRotating = false; text.text = ""; descriptionTextImage.SetActive(false); } IEnumerator SlowDown() { timeElapsed = 0; while (timeElapsed < slowDownLerpDuration) { valueToLerp = Mathf.Lerp(startValue, endValue, timeElapsed / slowDownLerpDuration); anim.SetFloat("Forward", valueToLerp); timeElapsed += Time.deltaTime; yield return null; } if (exited) { yield return new WaitForSeconds(1.5f); startRotating = true; StartCoroutine(SpeedUp()); } if (slowOnBack == false) { thirdPersonUserControl.enabled = true; } } IEnumerator SpeedUp() { timeElapsed = 0; while (timeElapsed < speedUpLerpDuration) { valueToLerp = Mathf.Lerp(endValue, startValue, timeElapsed / speedUpLerpDuration); anim.SetFloat("Forward", valueToLerp); timeElapsed += Time.deltaTime; yield return null; } } public IEnumerator DoRotate(float time) { var startRotation = transform.rotation; var newRotation = Quaternion.AngleAxis(180, Vector3.up) * transform.rotation; var progress = 0f; while (progress < 1) { progress += Time.deltaTime / time; transform.rotation = Quaternion.Slerp(startRotation, newRotation, progress); yield return null; } transform.rotation = newRotation; } }
玩家组件配置截图

相机控制脚本与配置
所有相机的父物体挂载了CamerasControl脚本,脚本挂载情况截图如下:
对应CamerasControl脚本代码:
using System.Collections; using System.Collections.Generic; using UnityEngine; using Cinemachine; public class CamerasControl : MonoBehaviour { public CinemachineFreeLook playerCameraGameplay; public float clampMin; public float clampMax; public float pitch; public float speed; void Update() { pitch += speed * Input.GetAxis("Mouse Y"); pitch = Mathf.Clamp(pitch, clampMin, clampMax); playerCameraGameplay.GetRig(1).GetCinemachineComponent<CinemachineComposer>().m_TrackedObjectOffset.y = pitch; } }
内容的提问来源于stack exchange,提问作者Daniel Lip
相关产品推荐
相关产品推荐

