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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 20:39:16