| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030 |
- #if !UNITY_2019_3_OR_NEWER
- #define CINEMACHINE_UNITY_IMGUI
- #endif
- using Cinemachine.Utility;
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.Events;
- using UnityEngine.SceneManagement;
- #if CINEMACHINE_HDRP || CINEMACHINE_URP
- #if CINEMACHINE_HDRP_7_3_1
- using UnityEngine.Rendering.HighDefinition;
- #else
- #if CINEMACHINE_URP
- using UnityEngine.Rendering.Universal;
- #else
- using UnityEngine.Experimental.Rendering.HDPipeline;
- #endif
- #endif
- #endif
- namespace Cinemachine
- {
- /// <summary>
- /// This interface is specifically for Timeline. Do not use it.
- /// </summary>
- public interface ICameraOverrideStack
- {
- /// <summary>
- /// Override the current camera and current blend. This setting will trump
- /// any in-game logic that sets virtual camera priorities and Enabled states.
- /// This is the main API for the timeline.
- /// </summary>
- /// <param name="overrideId">Id to represent a specific client. An internal
- /// stack is maintained, with the most recent non-empty override taking precenence.
- /// This id must be > 0. If you pass -1, a new id will be created, and returned.
- /// Use that id for subsequent calls. Don't forget to
- /// call ReleaseCameraOverride after all overriding is finished, to
- /// free the OverideStack resources.</param>
- /// <param name="camA">The camera to set, corresponding to weight=0.</param>
- /// <param name="camB">The camera to set, corresponding to weight=1.</param>
- /// <param name="weightB">The blend weight. 0=camA, 1=camB.</param>
- /// <param name="deltaTime">Override for deltaTime. Should be Time.FixedDelta for
- /// time-based calculations to be included, -1 otherwise.</param>
- /// <returns>The override ID. Don't forget to call ReleaseCameraOverride
- /// after all overriding is finished, to free the OverideStack resources.</returns>
- int SetCameraOverride(
- int overrideId,
- ICinemachineCamera camA, ICinemachineCamera camB,
- float weightB, float deltaTime);
- /// <summary>
- /// See SetCameraOverride. Call ReleaseCameraOverride after all overriding
- /// is finished, to free the OverrideStack resources.
- /// </summary>
- /// <param name="overrideId">The ID to released. This is the value that
- /// was returned by SetCameraOverride</param>
- void ReleaseCameraOverride(int overrideId);
- /// <summary>
- /// Get the current definition of Up. May be different from Vector3.up.
- /// </summary>
- Vector3 DefaultWorldUp { get; }
- }
- /// <summary>
- /// CinemachineBrain is the link between the Unity Camera and the Cinemachine Virtual
- /// Cameras in the scene. It monitors the priority stack to choose the current
- /// Virtual Camera, and blend with another if necessary. Finally and most importantly,
- /// it applies the Virtual Camera state to the attached Unity Camera.
- ///
- /// The CinemachineBrain is also the place where rules for blending between virtual cameras
- /// are defined. Camera blending is an interpolation over time of one virtual camera
- /// position and state to another. If you think of virtual cameras as cameramen, then
- /// blending is a little like one cameraman smoothly passing the camera to another cameraman.
- /// You can specify the time over which to blend, as well as the blend curve shape.
- /// Note that a camera cut is just a zero-time blend.
- /// </summary>
- [DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)]
- // [RequireComponent(typeof(Camera))] // strange but true: we can live without it
- [DisallowMultipleComponent]
- [ExecuteAlways]
- [AddComponentMenu("Cinemachine/CinemachineBrain")]
- [SaveDuringPlay]
- [HelpURL(Documentation.BaseURL + "manual/CinemachineBrainProperties.html")]
- public class CinemachineBrain : MonoBehaviour, ICameraOverrideStack
- {
- /// <summary>
- /// When enabled, the current camera and blend will be indicated in the
- /// game window, for debugging.
- /// </summary>
- [Tooltip("When enabled, the current camera and blend will be indicated in "
- + "the game window, for debugging")]
- public bool m_ShowDebugText = false;
- /// <summary>
- /// When enabled, shows the camera's frustum in the scene view.
- /// </summary>
- [Tooltip("When enabled, the camera's frustum will be shown at all times "
- + "in the scene view")]
- public bool m_ShowCameraFrustum = true;
- /// <summary>
- /// When enabled, the cameras will always respond in real-time to user input and damping,
- /// even if the game is running in slow motion
- /// </summary>
- [Tooltip("When enabled, the cameras will always respond in real-time to user input "
- + "and damping, even if the game is running in slow motion")]
- public bool m_IgnoreTimeScale = false;
- /// <summary>
- /// If set, this object's Y axis will define the worldspace Up vector for all the
- /// virtual cameras. This is useful in top-down game environments. If not set, Up is worldspace Y.
- /// </summary>
- [Tooltip("If set, this object's Y axis will define the worldspace Up vector for all the "
- + "virtual cameras. This is useful for instance in top-down game environments. "
- + "If not set, Up is worldspace Y. Setting this appropriately is important, "
- + "because Virtual Cameras don't like looking straight up or straight down.")]
- public Transform m_WorldUpOverride;
- /// <summary>This enum defines the options available for the update method.</summary>
- [DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)]
- public enum UpdateMethod
- {
- /// <summary>Virtual cameras are updated in sync with the Physics module, in FixedUpdate</summary>
- FixedUpdate,
- /// <summary>Virtual cameras are updated in MonoBehaviour LateUpdate.</summary>
- LateUpdate,
- /// <summary>Virtual cameras are updated according to how the target is updated.</summary>
- SmartUpdate,
- /// <summary>Virtual cameras are not automatically updated, client must explicitly call
- /// the CinemachineBrain's ManualUpdate() method.</summary>
- ManualUpdate
- };
- /// <summary>Depending on how the target objects are animated, adjust the update method to
- /// minimize the potential jitter. Use FixedUpdate if all your targets are animated with for RigidBody animation.
- /// SmartUpdate will choose the best method for each virtual camera, depending
- /// on how the target is animated.</summary>
- [Tooltip("The update time for the vcams. Use FixedUpdate if all your targets are animated "
- + "during FixedUpdate (e.g. RigidBodies), LateUpdate if all your targets are animated "
- + "during the normal Update loop, and SmartUpdate if you want Cinemachine to do the "
- + "appropriate thing on a per-target basis. SmartUpdate is the recommended setting")]
- public UpdateMethod m_UpdateMethod = UpdateMethod.SmartUpdate;
- /// <summary>This enum defines the options available for the update method.</summary>
- [DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)]
- public enum BrainUpdateMethod
- {
- /// <summary>Camera is updated in sync with the Physics module, in FixedUpdate</summary>
- FixedUpdate,
- /// <summary>Camera is updated in MonoBehaviour LateUpdate (or when ManualUpdate is called).</summary>
- LateUpdate
- };
- /// <summary>The update time for the Brain, i.e. when the blends are evaluated and the
- /// brain's transform is updated.</summary>
- [Tooltip("The update time for the Brain, i.e. when the blends are evaluated and "
- + "the brain's transform is updated")]
- public BrainUpdateMethod m_BlendUpdateMethod = BrainUpdateMethod.LateUpdate;
- /// <summary>
- /// The blend which is used if you don't explicitly define a blend between two Virtual Cameras.
- /// </summary>
- [CinemachineBlendDefinitionProperty]
- [Tooltip("The blend that is used in cases where you haven't explicitly defined a "
- + "blend between two Virtual Cameras")]
- public CinemachineBlendDefinition m_DefaultBlend
- = new CinemachineBlendDefinition(CinemachineBlendDefinition.Style.EaseInOut, 2f);
- /// <summary>
- /// This is the asset which contains custom settings for specific blends.
- /// </summary>
- [Tooltip("This is the asset that contains custom settings for blends between "
- + "specific virtual cameras in your scene")]
- public CinemachineBlenderSettings m_CustomBlends = null;
- /// <summary>
- /// Get the Unity Camera that is attached to this GameObject. This is the camera
- /// that will be controlled by the brain.
- /// </summary>
- public Camera OutputCamera
- {
- get
- {
- if (m_OutputCamera == null && !Application.isPlaying)
- ControlledObject.TryGetComponent(out m_OutputCamera);
- return m_OutputCamera;
- }
- }
- private Camera m_OutputCamera = null; // never use directly - use accessor
-
- /// <summary>
- /// CinemachineBrain controls this GameObject. Normally, this is the GameObject to which
- /// the CinemachineBrain component is attached. However, it is possible to override this
- /// by setting this property to another GameObject. If a Camera component is attached to the
- /// Controlled Object, then that Camera component's lens settings will also be driven
- /// by the CinemachineBrain.
- /// If this property is set to null, then CinemachineBrain is controlling the GameObject
- /// to which it is attached. The value of this property will always report as non-null.
- /// </summary>
- public GameObject ControlledObject
- {
- get => m_TargetOverride == null ? gameObject : m_TargetOverride;
- set
- {
- if (!ReferenceEquals(m_TargetOverride, value))
- {
- m_TargetOverride = value;
- ControlledObject.TryGetComponent(out m_OutputCamera); // update output camera when target changes
- }
- }
- }
- private GameObject m_TargetOverride = null; // never use directly - use accessor
- /// <summary>Event with a CinemachineBrain parameter</summary>
- [Serializable] public class BrainEvent : UnityEvent<CinemachineBrain> {}
- /// <summary>
- /// Event that is fired when a virtual camera is activated.
- /// The parameters are (incoming_vcam, outgoing_vcam), in that order.
- /// </summary>
- [Serializable] public class VcamActivatedEvent : UnityEvent<ICinemachineCamera, ICinemachineCamera> {}
- /// <summary>This event will fire whenever a virtual camera goes live and there is no blend</summary>
- [Tooltip("This event will fire whenever a virtual camera goes live and there is no blend")]
- public BrainEvent m_CameraCutEvent = new BrainEvent();
- /// <summary>This event will fire whenever a virtual camera goes live. If a blend is involved,
- /// then the event will fire on the first frame of the blend.
- ///
- /// The Parameters are (incoming_vcam, outgoing_vcam), in that order.</summary>
- [Tooltip("This event will fire whenever a virtual camera goes live. If a blend is "
- + "involved, then the event will fire on the first frame of the blend.")]
- public VcamActivatedEvent m_CameraActivatedEvent = new VcamActivatedEvent();
- /// <summary>
- /// API for the Unity Editor.
- /// Show this camera no matter what. This is static, and so affects all Cinemachine brains.
- /// </summary>
- public static ICinemachineCamera SoloCamera
- {
- get { return mSoloCamera; }
- set
- {
- if (value != null && !CinemachineCore.Instance.IsLive(value))
- value.OnTransitionFromCamera(null, Vector3.up, CinemachineCore.DeltaTime);
- mSoloCamera = value;
- }
- }
- /// <summary>API for the Unity Editor.</summary>
- /// <returns>Color used to indicate that a camera is in Solo mode.</returns>
- public static Color GetSoloGUIColor() { return Color.Lerp(Color.red, Color.yellow, 0.8f); }
- /// <summary>Get the default world up for the virtual cameras.</summary>
- public Vector3 DefaultWorldUp
- { get { return (m_WorldUpOverride != null) ? m_WorldUpOverride.transform.up : Vector3.up; } }
- private static ICinemachineCamera mSoloCamera;
- private Coroutine mPhysicsCoroutine;
- private int m_LastFrameUpdated;
- private void OnEnable()
- {
- // Make sure there is a first stack frame
- if (mFrameStack.Count == 0)
- mFrameStack.Add(new BrainFrame());
- CinemachineCore.Instance.AddActiveBrain(this);
- CinemachineDebug.OnGUIHandlers -= OnGuiHandler;
- CinemachineDebug.OnGUIHandlers += OnGuiHandler;
- // We check in after the physics system has had a chance to move things
- mPhysicsCoroutine = StartCoroutine(AfterPhysics());
- SceneManager.sceneLoaded += OnSceneLoaded;
- SceneManager.sceneUnloaded += OnSceneUnloaded;
- }
- private void OnDisable()
- {
- SceneManager.sceneLoaded -= OnSceneLoaded;
- SceneManager.sceneUnloaded -= OnSceneUnloaded;
- CinemachineDebug.OnGUIHandlers -= OnGuiHandler;
- CinemachineCore.Instance.RemoveActiveBrain(this);
- mFrameStack.Clear();
- StopCoroutine(mPhysicsCoroutine);
- }
- void OnSceneLoaded(Scene scene, LoadSceneMode mode)
- {
- if (Time.frameCount == m_LastFrameUpdated && mFrameStack.Count > 0)
- ManualUpdate();
- }
- void OnSceneUnloaded(Scene scene)
- {
- if (Time.frameCount == m_LastFrameUpdated && mFrameStack.Count > 0)
- ManualUpdate();
- }
-
- void Awake()
- {
- ControlledObject.TryGetComponent(out m_OutputCamera);
- }
-
- void Start()
- {
- m_LastFrameUpdated = -1;
- UpdateVirtualCameras(CinemachineCore.UpdateFilter.Late, -1f);
- }
- private void OnGuiHandler()
- {
- #if CINEMACHINE_UNITY_IMGUI
- if (!m_ShowDebugText)
- CinemachineDebug.ReleaseScreenPos(this);
- else
- {
- // Show the active camera and blend
- var sb = CinemachineDebug.SBFromPool();
- Color color = GUI.color;
- sb.Length = 0;
- sb.Append("CM ");
- sb.Append(gameObject.name);
- sb.Append(": ");
- if (SoloCamera != null)
- {
- sb.Append("SOLO ");
- GUI.color = GetSoloGUIColor();
- }
- if (IsBlending)
- sb.Append(ActiveBlend.Description);
- else
- {
- ICinemachineCamera vcam = ActiveVirtualCamera;
- if (vcam == null)
- sb.Append("(none)");
- else
- {
- sb.Append("[");
- sb.Append(vcam.Name);
- sb.Append("]");
- }
- }
- string text = sb.ToString();
- Rect r = CinemachineDebug.GetScreenPos(this, text, GUI.skin.box);
- GUI.Label(r, text, GUI.skin.box);
- GUI.color = color;
- CinemachineDebug.ReturnToPool(sb);
- }
- #endif
- }
- #if UNITY_EDITOR
- private void OnGUI()
- {
- if (CinemachineDebug.OnGUIHandlers != null)
- CinemachineDebug.OnGUIHandlers();
- }
- #endif
- WaitForFixedUpdate mWaitForFixedUpdate = new WaitForFixedUpdate();
- private IEnumerator AfterPhysics()
- {
- while (true)
- {
- // FixedUpdate can be called multiple times per frame
- yield return mWaitForFixedUpdate;
- if (m_UpdateMethod == UpdateMethod.FixedUpdate
- || m_UpdateMethod == UpdateMethod.SmartUpdate)
- {
- CinemachineCore.UpdateFilter filter = CinemachineCore.UpdateFilter.Fixed;
- if (m_UpdateMethod == UpdateMethod.SmartUpdate)
- {
- // Track the targets
- UpdateTracker.OnUpdate(UpdateTracker.UpdateClock.Fixed);
- filter = CinemachineCore.UpdateFilter.SmartFixed;
- }
- UpdateVirtualCameras(filter, GetEffectiveDeltaTime(true));
- }
- // Choose the active vcam and apply it to the Unity camera
- if (m_BlendUpdateMethod == BrainUpdateMethod.FixedUpdate)
- {
- UpdateFrame0(Time.fixedDeltaTime);
- ProcessActiveCamera(Time.fixedDeltaTime);
- }
- }
- }
- private void LateUpdate()
- {
- if (m_UpdateMethod != UpdateMethod.ManualUpdate)
- ManualUpdate();
- }
- /// <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()
- {
- m_LastFrameUpdated = Time.frameCount;
- float deltaTime = GetEffectiveDeltaTime(false);
- if (!Application.isPlaying || m_BlendUpdateMethod != BrainUpdateMethod.FixedUpdate)
- UpdateFrame0(deltaTime);
- ComputeCurrentBlend(ref mCurrentLiveCameras, 0);
- if (Application.isPlaying && 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.m_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 (!Application.isPlaying || m_BlendUpdateMethod != BrainUpdateMethod.FixedUpdate)
- ProcessActiveCamera(deltaTime);
- }
- #if UNITY_EDITOR
- /// This is only needed in editor mode to force timeline to call OnGUI while
- /// timeline is up and the game is not running, in order to allow dragging
- /// the composer guide in the game view.
- private void OnPreCull()
- {
- if (!Application.isPlaying)
- {
- // Note: this call will cause any screen canvas attached to the camera
- // to be painted one frame out of sync. It will only happen in the editor when not playing.
- ProcessActiveCamera(GetEffectiveDeltaTime(false));
- }
- }
- #endif
- private float GetEffectiveDeltaTime(bool fixedDelta)
- {
- if (CinemachineCore.UniformDeltaTimeOverride >= 0)
- return CinemachineCore.UniformDeltaTimeOverride;
- if (SoloCamera != null)
- return Time.unscaledDeltaTime;
- if (!Application.isPlaying)
- {
- for (int i = mFrameStack.Count - 1; i > 0; --i)
- {
- var frame = mFrameStack[i];
- if (frame.Active)
- return frame.deltaTimeOverride;
- }
- return -1;
- }
- if (m_IgnoreTimeScale)
- return fixedDelta ? Time.fixedDeltaTime : Time.unscaledDeltaTime;
- return fixedDelta ? Time.fixedDeltaTime : Time.deltaTime;
- }
- private void UpdateVirtualCameras(CinemachineCore.UpdateFilter updateFilter, float deltaTime)
- {
- // We always update all active virtual cameras
- CinemachineCore.Instance.m_CurrentUpdateFilter = updateFilter;
- Camera camera = OutputCamera;
- CinemachineCore.Instance.UpdateAllActiveVirtualCameras(
- camera == null ? -1 : camera.cullingMask, DefaultWorldUp, deltaTime);
- // Make sure all live cameras get updated, in case some of them are deactivated
- if (SoloCamera != null)
- SoloCamera.UpdateCameraState(DefaultWorldUp, deltaTime);
- mCurrentLiveCameras.UpdateCameraState(DefaultWorldUp, deltaTime);
- // Restore the filter for general use
- updateFilter = CinemachineCore.UpdateFilter.Late;
- if (Application.isPlaying)
- {
- if (m_UpdateMethod == UpdateMethod.SmartUpdate)
- updateFilter |= CinemachineCore.UpdateFilter.Smart;
- else if (m_UpdateMethod == UpdateMethod.FixedUpdate)
- updateFilter = CinemachineCore.UpdateFilter.Fixed;
- }
- CinemachineCore.Instance.m_CurrentUpdateFilter = updateFilter;
- }
- /// <summary>
- /// Get the current active virtual camera.
- /// </summary>
- public ICinemachineCamera ActiveVirtualCamera
- {
- get
- {
- if (SoloCamera != null)
- return SoloCamera;
- return DeepCamBFromBlend(mCurrentLiveCameras);
- }
- }
- static ICinemachineCamera DeepCamBFromBlend(CinemachineBlend blend)
- {
- ICinemachineCamera vcam = blend.CamB;
- while (vcam != null)
- {
- if (!vcam.IsValid)
- return null; // deleted!
- BlendSourceVirtualCamera bs = vcam as BlendSourceVirtualCamera;
- if (bs == null)
- break;
- vcam = bs.Blend.CamB;
- }
- return vcam;
- }
- /// <summary>
- /// Checks if the vcam is live as part of an outgoing blend.
- /// Does not check whether the vcam is also the current active vcam.
- /// </summary>
- /// <param name="vcam">The virtual camera to check</param>
- /// <returns>True if the virtual camera is part of a live outgoing blend, false otherwise</returns>
- public bool IsLiveInBlend(ICinemachineCamera vcam)
- {
- // Ignore mCurrentLiveCameras.CamB
- if (vcam == mCurrentLiveCameras.CamA)
- return true;
- var b = mCurrentLiveCameras.CamA as BlendSourceVirtualCamera;
- if (b != null && b.Blend.Uses(vcam))
- return true;
- ICinemachineCamera parent = vcam.ParentCamera;
- if (parent != null && parent.IsLiveChild(vcam, false))
- return IsLiveInBlend(parent);
- return false;
- }
- /// <summary>
- /// Is there a blend in progress?
- /// </summary>
- public bool IsBlending { get { return ActiveBlend != null; } }
- /// <summary>
- /// Get the current blend in progress. Returns null if none.
- /// It is also possible to set the current blend, but this is not a recommended usage.
- /// </summary>
- public CinemachineBlend ActiveBlend
- {
- get
- {
- if (SoloCamera != null)
- return null;
- if (mCurrentLiveCameras.CamA == null || mCurrentLiveCameras.Equals(null) || mCurrentLiveCameras.IsComplete)
- return null;
- return mCurrentLiveCameras;
- }
- set
- {
- if (value == null)
- mFrameStack[0].blend.Duration = 0;
- else
- mFrameStack[0].blend = value;
- }
- }
- private class BrainFrame
- {
- public int id;
- public CinemachineBlend blend = new CinemachineBlend(null, null, null, 0, 0);
- public bool Active { get { return blend.IsValid; } }
- // Working data - updated every frame
- public CinemachineBlend workingBlend = new CinemachineBlend(null, null, null, 0, 0);
- public BlendSourceVirtualCamera workingBlendSource = new BlendSourceVirtualCamera(null);
- // Used by Timeline Preview for overriding the current value of deltaTime
- public float deltaTimeOverride;
- // Used for blend reversal. Range is 0...1,
- // representing where the blend started when reversed mid-blend
- public float blendStartPosition;
- }
- // Current game state is always frame 0, overrides are subsequent frames
- private List<BrainFrame> mFrameStack = new List<BrainFrame>();
- private int mNextFrameId = 1;
- /// Get the frame index corresponding to the ID
- private int GetBrainFrame(int withId)
- {
- int count = mFrameStack.Count;
- for (int i = count - 1; i > 0; --i)
- if (mFrameStack[i].id == withId)
- return i;
- // Not found - add it
- mFrameStack.Add(new BrainFrame() { id = withId });
- return mFrameStack.Count - 1;
- }
- // Current Brain State - result of all frames. Blend camB is "current" camera always
- CinemachineBlend mCurrentLiveCameras = new CinemachineBlend(null, null, null, 0, 0);
-
- // To avoid GC memory alloc every frame
- private static readonly AnimationCurve mDefaultLinearAnimationCurve = AnimationCurve.Linear(0, 0, 1, 1);
-
- /// <summary>
- /// This API is specifically for Timeline. Do not use it.
- /// Override the current camera and current blend. This setting will trump
- /// any in-game logic that sets virtual camera priorities and Enabled states.
- /// This is the main API for the timeline.
- /// </summary>
- /// <param name="overrideId">Id to represent a specific client. An internal
- /// stack is maintained, with the most recent non-empty override taking precenence.
- /// This id must be > 0. If you pass -1, a new id will be created, and returned.
- /// Use that id for subsequent calls. Don't forget to
- /// call ReleaseCameraOverride after all overriding is finished, to
- /// free the OverideStack resources.</param>
- /// <param name="camA"> The camera to set, corresponding to weight=0</param>
- /// <param name="camB"> The camera to set, corresponding to weight=1</param>
- /// <param name="weightB">The blend weight. 0=camA, 1=camB</param>
- /// <param name="deltaTime">override for deltaTime. Should be Time.FixedDelta for
- /// time-based calculations to be included, -1 otherwise</param>
- /// <returns>The oiverride ID. Don't forget to call ReleaseCameraOverride
- /// after all overriding is finished, to free the OverideStack resources.</returns>
- public int SetCameraOverride(
- int overrideId,
- ICinemachineCamera camA, ICinemachineCamera camB,
- float weightB, float deltaTime)
- {
- if (overrideId < 0)
- overrideId = mNextFrameId++;
- BrainFrame frame = mFrameStack[GetBrainFrame(overrideId)];
- frame.deltaTimeOverride = deltaTime;
- frame.blend.CamA = camA;
- frame.blend.CamB = camB;
- frame.blend.BlendCurve = mDefaultLinearAnimationCurve;
- frame.blend.Duration = 1;
- frame.blend.TimeInBlend = weightB;
- // In case vcams are inactive game objects, make sure they get initialized properly
- var cam = camA as CinemachineVirtualCameraBase;
- if (cam != null)
- cam.EnsureStarted();
- cam = camB as CinemachineVirtualCameraBase;
- if (cam != null)
- cam.EnsureStarted();
- return overrideId;
- }
- /// <summary>
- /// This API is specifically for Timeline. Do not use it.
- /// Release the resources used for a camera override client.
- /// See SetCameraOverride.
- /// </summary>
- /// <param name="overrideId">The ID to released. This is the value that
- /// was returned by SetCameraOverride</param>
- public void ReleaseCameraOverride(int overrideId)
- {
- for (int i = mFrameStack.Count - 1; i > 0; --i)
- {
- if (mFrameStack[i].id == overrideId)
- {
- mFrameStack.RemoveAt(i);
- return;
- }
- }
- }
- ICinemachineCamera mActiveCameraPreviousFrame;
- GameObject mActiveCameraPreviousFrameGameObject;
- private void ProcessActiveCamera(float deltaTime)
- {
- var activeCamera = ActiveVirtualCamera;
- if (SoloCamera != null)
- {
- var state = SoloCamera.State;
- PushStateToUnityCamera(ref state);
- }
- else if (activeCamera == null)
- {
- // No active virtal camera. We create a state representing its position
- // and call the callback, but we don't actively set the transform or lens
- var state = CameraState.Default;
- var target = ControlledObject.transform;
- state.RawPosition = target.position;
- state.RawOrientation = target.rotation;
- state.Lens = LensSettings.FromCamera(m_OutputCamera);
- state.BlendHint |= CameraState.BlendHintValue.NoTransform | CameraState.BlendHintValue.NoLens;
- PushStateToUnityCamera(ref state);
- }
- else
- {
- // Has the current camera changed this frame?
- if (mActiveCameraPreviousFrameGameObject == null)
- mActiveCameraPreviousFrame = null; // object was deleted
- if (activeCamera != mActiveCameraPreviousFrame)
- {
- // Notify incoming camera of transition
- activeCamera.OnTransitionFromCamera(
- mActiveCameraPreviousFrame, DefaultWorldUp, deltaTime);
- if (m_CameraActivatedEvent != null)
- m_CameraActivatedEvent.Invoke(activeCamera, mActiveCameraPreviousFrame);
- // If we're cutting without a blend, send an event
- if (!IsBlending || (mActiveCameraPreviousFrame != null
- && !ActiveBlend.Uses(mActiveCameraPreviousFrame)))
- {
- if (m_CameraCutEvent != null)
- m_CameraCutEvent.Invoke(this);
- if (CinemachineCore.CameraCutEvent != null)
- CinemachineCore.CameraCutEvent.Invoke(this);
- }
- // Re-update in case it's inactive
- activeCamera.UpdateCameraState(DefaultWorldUp, deltaTime);
- }
- // Apply the vcam state to the Unity camera
- var state = mCurrentLiveCameras.State;
- PushStateToUnityCamera(ref state);
- }
- mActiveCameraPreviousFrame = activeCamera;
- mActiveCameraPreviousFrameGameObject
- = activeCamera == null ? null : activeCamera.VirtualCameraGameObject;
- }
- private void UpdateFrame0(float deltaTime)
- {
- // Make sure there is a first stack frame
- if (mFrameStack.Count == 0)
- mFrameStack.Add(new BrainFrame());
- // Update the in-game frame (frame 0)
- BrainFrame frame = mFrameStack[0];
- // Are we transitioning cameras?
- var activeCamera = TopCameraFromPriorityQueue();
- var outGoingCamera = frame.blend.CamB;
- if (activeCamera != outGoingCamera)
- {
- // Do we need to create a game-play blend?
- if ((UnityEngine.Object)activeCamera != null
- && (UnityEngine.Object)outGoingCamera != null && deltaTime >= 0)
- {
- // Create a blend (curve will be null if a cut)
- var blendDef = LookupBlend(outGoingCamera, activeCamera);
- float blendDuration = blendDef.BlendTime;
- float blendStartPosition = 0;
- if (blendDef.BlendCurve != null && blendDuration > UnityVectorExtensions.Epsilon)
- {
- if (frame.blend.IsComplete)
- frame.blend.CamA = outGoingCamera; // new blend
- else
- {
- // Special case: if backing out of a blend-in-progress
- // with the same blend in reverse, adjust the blend time
- // to cancel out the progress made in the opposite direction
- if ((frame.blend.CamA == activeCamera
- || (frame.blend.CamA as BlendSourceVirtualCamera)?.Blend.CamB == activeCamera)
- && frame.blend.CamB == outGoingCamera)
- {
- // How far have we blended? That is what we must undo
- var progress = frame.blendStartPosition
- + (1 - frame.blendStartPosition) * frame.blend.TimeInBlend / frame.blend.Duration;
- blendDuration *= progress;
- blendStartPosition = 1 - progress;
- }
- // Chain to existing blend
- frame.blend.CamA = new BlendSourceVirtualCamera(
- new CinemachineBlend(
- frame.blend.CamA, frame.blend.CamB,
- frame.blend.BlendCurve, frame.blend.Duration,
- frame.blend.TimeInBlend));
- }
- }
- frame.blend.BlendCurve = blendDef.BlendCurve;
- frame.blend.Duration = blendDuration;
- frame.blend.TimeInBlend = 0;
- frame.blendStartPosition = blendStartPosition;
- }
- // Set the current active camera
- frame.blend.CamB = activeCamera;
- }
- // Advance the current blend (if any)
- if (frame.blend.CamA != null)
- {
- frame.blend.TimeInBlend += (deltaTime >= 0) ? deltaTime : frame.blend.Duration;
- if (frame.blend.IsComplete)
- {
- // No more blend
- frame.blend.CamA = null;
- frame.blend.BlendCurve = null;
- frame.blend.Duration = 0;
- frame.blend.TimeInBlend = 0;
- }
- }
- }
- /// <summary>
- /// Used internally to compute the currrent blend, taking into account
- /// the in-game camera and all the active overrides. Caller may optionally
- /// exclude n topmost overrides.
- /// </summary>
- /// <param name="outputBlend">Receives the nested blend</param>
- /// <param name="numTopLayersToExclude">Optionaly exclude the last number
- /// of overrides from the blend</param>
- public void ComputeCurrentBlend(
- ref CinemachineBlend outputBlend, int numTopLayersToExclude)
- {
- // Make sure there is a first stack frame
- if (mFrameStack.Count == 0)
- mFrameStack.Add(new BrainFrame());
- // Resolve the current working frame states in the stack
- int lastActive = 0;
- int topLayer = Mathf.Max(1, mFrameStack.Count - numTopLayersToExclude);
- for (int i = 0; i < topLayer; ++i)
- {
- BrainFrame frame = mFrameStack[i];
- if (i == 0 || frame.Active)
- {
- frame.workingBlend.CamA = frame.blend.CamA;
- frame.workingBlend.CamB = frame.blend.CamB;
- frame.workingBlend.BlendCurve = frame.blend.BlendCurve;
- frame.workingBlend.Duration = frame.blend.Duration;
- frame.workingBlend.TimeInBlend = frame.blend.TimeInBlend;
- if (i > 0 && !frame.blend.IsComplete)
- {
- if (frame.workingBlend.CamA == null)
- {
- if (mFrameStack[lastActive].blend.IsComplete)
- frame.workingBlend.CamA = mFrameStack[lastActive].blend.CamB;
- else
- {
- frame.workingBlendSource.Blend = mFrameStack[lastActive].workingBlend;
- frame.workingBlend.CamA = frame.workingBlendSource;
- }
- }
- else if (frame.workingBlend.CamB == null)
- {
- if (mFrameStack[lastActive].blend.IsComplete)
- frame.workingBlend.CamB = mFrameStack[lastActive].blend.CamB;
- else
- {
- frame.workingBlendSource.Blend = mFrameStack[lastActive].workingBlend;
- frame.workingBlend.CamB = frame.workingBlendSource;
- }
- }
- }
- lastActive = i;
- }
- }
- var workingBlend = mFrameStack[lastActive].workingBlend;
- outputBlend.CamA = workingBlend.CamA;
- outputBlend.CamB = workingBlend.CamB;
- outputBlend.BlendCurve = workingBlend.BlendCurve;
- outputBlend.Duration = workingBlend.Duration;
- outputBlend.TimeInBlend = workingBlend.TimeInBlend;
- }
- /// <summary>
- /// True if the ICinemachineCamera the current active camera,
- /// or part of a current blend, either directly or indirectly because its parents are live.
- /// </summary>
- /// <param name="vcam">The camera to test whether it is live</param>
- /// <param name="dominantChildOnly">If true, will only return true if this vcam is the dominat live child</param>
- /// <returns>True if the camera is live (directly or indirectly)
- /// or part of a blend in progress.</returns>
- public bool IsLive(ICinemachineCamera vcam, bool dominantChildOnly = false)
- {
- if (SoloCamera == vcam)
- return true;
- if (mCurrentLiveCameras.Uses(vcam))
- return true;
- ICinemachineCamera parent = vcam.ParentCamera;
- while (parent != null && parent.IsLiveChild(vcam, dominantChildOnly))
- {
- if (SoloCamera == parent || mCurrentLiveCameras.Uses(parent))
- return true;
- vcam = parent;
- parent = vcam.ParentCamera;
- }
- return false;
- }
- /// <summary>
- /// The current state applied to the unity camera (may be the result of a blend)
- /// </summary>
- public CameraState CurrentCameraState { get; private set; }
- /// <summary>
- /// Get the highest-priority Enabled ICinemachineCamera
- /// that is visible to my camera. Culling Mask is used to test visibility.
- /// </summary>
- /// <returns>The highest-priority Enabled ICinemachineCamera that is in my visible layers.</returns>
- protected virtual ICinemachineCamera TopCameraFromPriorityQueue()
- {
- CinemachineCore core = CinemachineCore.Instance;
- Camera outputCamera = OutputCamera;
- int mask = outputCamera == null ? ~0 : outputCamera.cullingMask;
- int numCameras = core.VirtualCameraCount;
- for (int i = 0; i < numCameras; ++i)
- {
- var cam = core.GetVirtualCamera(i);
- GameObject go = cam != null ? cam.gameObject : null;
- if (go != null && (mask & (1 << go.layer)) != 0)
- return cam;
- }
- return null;
- }
- /// <summary>
- /// Create a blend curve for blending from one ICinemachineCamera to another.
- /// If there is a specific blend defined for these cameras it will be used, otherwise
- /// a default blend will be created, which could be a cut.
- /// </summary>
- private CinemachineBlendDefinition LookupBlend(
- ICinemachineCamera fromKey, ICinemachineCamera toKey)
- {
- // Get the blend curve that's most appropriate for these cameras
- CinemachineBlendDefinition blend = m_DefaultBlend;
- if (m_CustomBlends != null)
- {
- string fromCameraName = (fromKey != null) ? fromKey.Name : string.Empty;
- string toCameraName = (toKey != null) ? toKey.Name : string.Empty;
- blend = m_CustomBlends.GetBlendForVirtualCameras(
- fromCameraName, toCameraName, blend);
- }
- if (CinemachineCore.GetBlendOverride != null)
- blend = CinemachineCore.GetBlendOverride(fromKey, toKey, blend, this);
- return blend;
- }
- /// <summary> Apply a cref="CameraState"/> to the game object</summary>
- private void PushStateToUnityCamera(ref CameraState state)
- {
- CurrentCameraState = state;
- var target = ControlledObject.transform;
- var pos = target.position;
- var rot = target.rotation;
- if ((state.BlendHint & CameraState.BlendHintValue.NoPosition) == 0)
- pos = state.FinalPosition;
- if ((state.BlendHint & CameraState.BlendHintValue.NoOrientation) == 0)
- rot = state.FinalOrientation;
- // Avoid dirtying the scene with insignificant rotations diffs
- target.ConservativeSetPositionAndRotation(pos, rot);
- if ((state.BlendHint & CameraState.BlendHintValue.NoLens) == 0)
- {
- Camera cam = OutputCamera;
- if (cam != null)
- {
- cam.nearClipPlane = state.Lens.NearClipPlane;
- cam.farClipPlane = state.Lens.FarClipPlane;
- cam.orthographicSize = state.Lens.OrthographicSize;
- cam.fieldOfView = state.Lens.FieldOfView;
- cam.lensShift = state.Lens.LensShift;
- if (state.Lens.ModeOverride != LensSettings.OverrideModes.None)
- cam.orthographic = state.Lens.Orthographic;
- bool isPhysical = state.Lens.ModeOverride == LensSettings.OverrideModes.None
- ? cam.usePhysicalProperties : state.Lens.IsPhysicalCamera;
- cam.usePhysicalProperties = isPhysical;
- if (isPhysical && state.Lens.IsPhysicalCamera)
- {
- cam.sensorSize = state.Lens.SensorSize;
- cam.gateFit = state.Lens.GateFit;
- cam.focalLength = Camera.FieldOfViewToFocalLength(state.Lens.FieldOfView, state.Lens.SensorSize.y);
- #if UNITY_2022_2_OR_NEWER
- cam.focusDistance = state.Lens.FocusDistance;
- #endif
- #if CINEMACHINE_HDRP
- #if CINEMACHINE_HDRP_14
- cam.iso = state.Lens.Iso;
- cam.shutterSpeed = state.Lens.ShutterSpeed;
- cam.aperture = state.Lens.Aperture;
- cam.bladeCount = state.Lens.BladeCount;
- cam.curvature = state.Lens.Curvature;
- cam.barrelClipping = state.Lens.BarrelClipping;
- cam.anamorphism = state.Lens.Anamorphism;
- #else
- cam.TryGetComponent<HDAdditionalCameraData>(out var hda);
- if (hda != null)
- {
- hda.physicalParameters.iso = state.Lens.Iso;
- hda.physicalParameters.shutterSpeed = state.Lens.ShutterSpeed;
- hda.physicalParameters.aperture = state.Lens.Aperture;
- hda.physicalParameters.bladeCount = state.Lens.BladeCount;
- hda.physicalParameters.curvature = state.Lens.Curvature;
- hda.physicalParameters.barrelClipping = state.Lens.BarrelClipping;
- hda.physicalParameters.anamorphism = state.Lens.Anamorphism;
- }
- #endif
- #endif
- }
- }
- }
- if (CinemachineCore.CameraUpdatedEvent != null)
- CinemachineCore.CameraUpdatedEvent.Invoke(this);
- }
- }
- }
|