【Unity】使Cinemachine立即完成Damp、当前Blend和即将发生的Blend

这篇博客介绍了如何通过修改Cinemachine源码,使得在Unity中可以立即完成相机的Damp和Blend过程,适用于需要动态定位到新生成角色的情况。作者分享了具体的代码实现,包括完成当前Blend和即将到来的Blend的方法,并提供了基于Cinemachine 2.6.5版本的源码修改示例。此外,还提到了官方在新版本中已内置此功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【Unity】使Cinemachine立即完成Damp、当前Blend和即将发生的Blend

2022.11.13补充:
Unity官方回复说新版Cinemachine已经内置此功能:
https://forum.unity.com/threads/how-to-make-cinemachine-complete-current-blending-immediately.1131133/#post-8581039

2022.11.18补充:
论坛上有人对下面的方法给出了一些补充建议:
https://forum.unity.com/threads/how-to-make-cinemachine-complete-current-blending-immediately.1131133/#post-8567951

游戏中很多时候都会动态生成玩家角色,这时会希望相机立即定位到刚刚生成的玩家角色,不要有从远处将镜头Blend过来或者Damp的过程,但Cinemachine直到2.8.0版本都没有提供立即完成Blend的方法。这里对Cinemachine的源码做了些微小的改动,使其能够立即完成Damp、当前Blend和即将发生的Blend。

目前版本的Cinemachine已经含有使相机立即完成Damp的功能,只需要将 cinemachineVirtualCameraBase.PreviousStateIsValid 设为 false 即可让虚拟相机在下一帧立即完成Damp。但将Damp延迟到下一帧完成,有时可能导致画面跳变,因此下面的完成Blend的代码对其进行了整合,可以使Damp和Blend都在调用方法的当帧完成。

首先需要将Cinemachine的源码从 Library 文件夹移到 Packages 文件夹中,然后将 CinemachineBrain 类改为 partial 类型,之后的修改都不会再动到Cinemachine的核心源码。

接下来新建一个 partialCinemachineBrain 类,在其中填充完成Blend和Damp的代码。下面贴出的源代码中已经包含了主要步骤的注释,因此这里不再描述细节,只说一些使用限制:下面的代码是基于Cinemachine 2.6.5版本写的,依赖了从这个版本才加入的 ManualUpdate() 方法,如果是低版本的Cinemachine,将 ManualUpdate() 方法换成 LateUpdate() 应该也可以生效(我没有测试过低版本)。

源代码

namespace Cinemachine
{
    public partial class CinemachineBrain
    {
        /// <summary>
        /// Call this method explicitly from an external script to update the virtual cameras
        /// and position the main camera, if the UpdateMode is set to ManualUpdate.
        /// For other update modes, this method is called automatically, and should not be
        /// called from elsewhere.
        /// </summary>
        public void ManualUpdate(float deltaTime)
        {
            if (m_BlendUpdateMethod != BrainUpdateMethod.FixedUpdate)
                UpdateFrame0(deltaTime);

            ComputeCurrentBlend(ref mCurrentLiveCameras, 0);

            if (m_UpdateMethod == UpdateMethod.FixedUpdate)
            {
                // Special handling for fixed update: cameras that have been enabled
                // since the last physics frame must be updated now
                if (m_BlendUpdateMethod != BrainUpdateMethod.FixedUpdate)
                {
                    CinemachineCore.Instance.CurrentUpdateFilter = CinemachineCore.UpdateFilter.Fixed;
                    if (SoloCamera == null)
                        mCurrentLiveCameras.UpdateCameraState(
                            DefaultWorldUp, GetEffectiveDeltaTime(true));
                }
            }
            else
            {
                CinemachineCore.UpdateFilter filter = CinemachineCore.UpdateFilter.Late;
                if (m_UpdateMethod == UpdateMethod.SmartUpdate)
                {
                    // Track the targets
                    UpdateTracker.OnUpdate(UpdateTracker.UpdateClock.Late);
                    filter = CinemachineCore.UpdateFilter.SmartLate;
                }
                UpdateVirtualCameras(filter, deltaTime);
            }

            // Choose the active vcam and apply it to the Unity camera
            if (m_BlendUpdateMethod != BrainUpdateMethod.FixedUpdate)
                ProcessActiveCamera(deltaTime);
        }

        /// <summary>
        /// Complete current active blend immediately.
        /// </summary>
        /// <param name="completeDamp">If true, will also complete aim and body damp.</param>
        public void CompleteCurrentBlend(bool completeDamp = true)
        {
            if (mFrameStack.Count == 0)
            {
                return;
            }

            // Complete current blend
            mFrameStack[0].blend.Duration = 0;
            ManualUpdate();

            // Use -1 to prevent dolly camera finds closest point
            var deltaTime = -1;
            CinemachineCore.UpdateFilter filter = CinemachineCore.UpdateFilter.Late;
            if (m_UpdateMethod == UpdateMethod.SmartUpdate)
            {
                // Track the targets
                UpdateTracker.OnUpdate(UpdateTracker.UpdateClock.Late);
                filter = CinemachineCore.UpdateFilter.SmartLate;
            }
            UpdateVirtualCameras(filter, deltaTime);

            // Choose the active vcam and apply it to the Unity camera
            if (m_BlendUpdateMethod != BrainUpdateMethod.FixedUpdate)
                ProcessActiveCamera(deltaTime);

            // Complete damp
            if (completeDamp && ActiveVirtualCamera is CinemachineVirtualCameraBase vcam)
            {
                vcam.PreviousStateIsValid = false;
                ManualUpdate();
            }
        }

        /// <summary>
        /// Complete incoming active blend immediately if the incoming blend is between camA and camB or contains camA or camB.
        /// </summary>
        /// <param name="appointCamera">If not null, will only take effect when top priority camera equals appointCamera.</param>
        /// <param name="completeDamp">If true, will also complete aim and body damp.</param>
        public void CompleteIncomingBlend(CinemachineVirtualCameraBase appointCamera = null, bool completeDamp = true)
        {
            if (mFrameStack.Count == 0)
            {
                return;
            }

            if (appointCamera)
            {
                appointCamera.MoveToTopOfPrioritySubqueue();
            }

            // Find new active camera
            var topCam = ActiveVirtualCamera as CinemachineVirtualCameraBase;
            int numCameras = CinemachineCore.Instance.VirtualCameraCount;
            for (int i = 0; i < numCameras; ++i)
            {
                var cam = CinemachineCore.Instance.GetVirtualCamera(i);
                if (cam.gameObject.scene != gameObject.scene)
                {
                    continue;
                }

                if (!topCam || topCam.gameObject.scene != gameObject.scene || topCam.Priority < cam.Priority)
                {
                    topCam = cam;
                }

                // When call MoveToTopOfPrioritySubqueue() and ManualUpdate(),
                // cameras in mActiveCameras may out of order, so do not break
                //if (topCam.Priority >= cam.Priority)
                //{
                //    break;
                //}
            }
            if (topCam == null || (appointCamera && appointCamera != topCam))
            {
                return;
            }

            // Trigger virtual cameras priority queen update
            topCam.MoveToTopOfPrioritySubqueue();

            // Update frame stack
            UpdateFrame0(0);

            // Complete blend
            CompleteCurrentBlend(completeDamp);
        }
    }

    public static class CinemachineCameraExtension
    {
        /// <summary>
        /// Complete body and aim damp.
        /// </summary>
        /// <param name="cmCam"></param>
        public static void CompleteDamps(this ICinemachineCamera cmCam)
        {
            if (cmCam is CinemachineVirtualCameraBase vcam)
            {
                vcam.PreviousStateIsValid = false;
            }
        }
    }
}
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值