//#define RESET_PROJECTION_MATRIX // GML todo: decide on the correct solution using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.Serialization; namespace Unity.Cinemachine { /// <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> [DisallowMultipleComponent] [ExecuteAlways] [AddComponentMenu("Cinemachine/Cinemachine Brain")] [SaveDuringPlay] [HelpURL(Documentation.BaseURL + "manual/CinemachineBrain.html")] public class CinemachineBrain : MonoBehaviour, ICameraOverrideStack, ICinemachineMixer { /// <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")] [FormerlySerializedAs("m_ShowDebugText")] public bool 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")] [FormerlySerializedAs("m_ShowCameraFrustum")] public bool 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")] [FormerlySerializedAs("m_IgnoreTimeScale")] public bool IgnoreTimeScale = false; /// <summary> /// If set, this object's Y axis will define the world-space Up vector for all the /// virtual cameras. This is useful in top-down game environments. If not set, Up is world-space Y. /// </summary> [Tooltip("If set, this object's Y axis will define the world-space Up vector for all the " + "virtual cameras. This is useful for instance in top-down game environments. " + "If not set, Up is world-space Y. Setting this appropriately is important, " + "because Virtual Cameras don't like looking straight up or straight down.")] [FormerlySerializedAs("m_WorldUpOverride")] public Transform WorldUpOverride; /// <summary>The CinemachineBrain will find the highest-priority CinemachineCamera that outputs /// to any of the channels selected. CinemachineCameras that do not output to one of these /// channels will be ignored. Use this in situations where multiple CinemachineBrains are /// needed (for example, Split-screen).</summary> [Tooltip("The CinemachineBrain will find the highest-priority CinemachineCamera that outputs to " + "any of the channels selected. CinemachineCameras that do not output to one of these " + "channels will be ignored. Use this in situations where multiple CinemachineBrains are " + "needed (for example, Split-screen).")] public OutputChannels ChannelMask = (OutputChannels)(-1); // default is Everything /// <summary>This enum defines the options available for the update method.</summary> public enum UpdateMethods { /// <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")] [FormerlySerializedAs("m_UpdateMethod")] public UpdateMethods UpdateMethod = UpdateMethods.SmartUpdate; /// <summary>This enum defines the options available for the update method.</summary> public enum BrainUpdateMethods { /// <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")] [FormerlySerializedAs("m_BlendUpdateMethod")] public BrainUpdateMethods BlendUpdateMethod = BrainUpdateMethods.LateUpdate; /// <summary>Defines the settings for Lens Mode overriding</summary> [Serializable] public struct LensModeOverrideSettings { /// <summary>If set, will enable CM cameras to override the lens mode of the camera</summary> [Tooltip("If set, will enable CM cameras to override the lens mode of the camera")] public bool Enabled; /// <summary>Lens mode to use when no mode override is active</summary> [Tooltip("Lens mode to use when no mode override is active")] public LensSettings.OverrideModes DefaultMode; } /// <summary>Controls whether CM cameras can change the lens mode.</summary> [FoldoutWithEnabledButton] public LensModeOverrideSettings LensModeOverride = new () { DefaultMode = LensSettings.OverrideModes.Perspective }; /// <summary> /// The blend which is used if you don't explicitly define a blend between two Virtual Cameras. /// </summary> [Tooltip("The blend that is used in cases where you haven't explicitly defined a " + "blend between two Virtual Cameras")] [FormerlySerializedAs("m_DefaultBlend")] public CinemachineBlendDefinition DefaultBlend = new (CinemachineBlendDefinition.Styles.EaseInOut, 2f); /// <summary> /// This is the asset that contains custom settings for blends between /// specific virtual cameras in your scene. /// </summary> [Tooltip("This is the asset that contains custom settings for blends between " + "specific virtual cameras in your scene")] [FormerlySerializedAs("m_CustomBlends")] [EmbeddedBlenderSettingsProperty] public CinemachineBlenderSettings CustomBlends = null; Camera m_OutputCamera = null; // never use directly - use accessor GameObject m_TargetOverride = null; // never use directly - use accessor int m_LastFrameUpdated; Coroutine m_PhysicsCoroutine; readonly WaitForFixedUpdate m_WaitForFixedUpdate = new (); readonly BlendManager m_BlendManager = new (); static readonly List<CinemachineBrain> s_ActiveBrains = new (); CameraState m_CameraState; // Cached camera state #if CINEMACHINE_UIELEMENTS && UNITY_EDITOR DebugText m_DebugText; #endif void OnValidate() { DefaultBlend.Time = Mathf.Max(0, DefaultBlend.Time); } void Reset() { DefaultBlend = new CinemachineBlendDefinition(CinemachineBlendDefinition.Styles.EaseInOut, 2f); CustomBlends = null; ShowDebugText = false; ShowCameraFrustum = true; IgnoreTimeScale = false; WorldUpOverride = null; ChannelMask = (OutputChannels)(-1); UpdateMethod = UpdateMethods.SmartUpdate; BlendUpdateMethod = BrainUpdateMethods.LateUpdate; LensModeOverride = new LensModeOverrideSettings { DefaultMode = LensSettings.OverrideModes.Perspective }; } void Awake() { ControlledObject.TryGetComponent(out m_OutputCamera); } void Start() { m_LastFrameUpdated = -1; UpdateVirtualCameras(CameraUpdateManager.UpdateFilter.Late, -1f); } void OnEnable() { m_BlendManager.OnEnable(); m_BlendManager.LookupBlendDelegate = LookupBlend; s_ActiveBrains.Add(this); #if UNITY_EDITOR && CINEMACHINE_UIELEMENTS CinemachineDebug.OnGUIHandlers -= OnGuiHandler; CinemachineDebug.OnGUIHandlers += OnGuiHandler; #endif // We check in after the physics system has had a chance to move things m_PhysicsCoroutine = StartCoroutine(AfterPhysics()); SceneManager.sceneLoaded += OnSceneLoaded; SceneManager.sceneUnloaded += OnSceneUnloaded; } void OnDisable() { SceneManager.sceneLoaded -= OnSceneLoaded; SceneManager.sceneUnloaded -= OnSceneUnloaded; #if UNITY_EDITOR && CINEMACHINE_UIELEMENTS CinemachineDebug.OnGUIHandlers -= OnGuiHandler; m_DebugText?.Dispose(); m_DebugText = null; #endif s_ActiveBrains.Remove(this); m_BlendManager.OnDisable(); StopCoroutine(m_PhysicsCoroutine); } void OnSceneLoaded(Scene scene, LoadSceneMode mode) { if (Time.frameCount == m_LastFrameUpdated && m_BlendManager.IsInitialized) ManualUpdate(); } void OnSceneUnloaded(Scene scene) { if (Time.frameCount == m_LastFrameUpdated && m_BlendManager.IsInitialized) ManualUpdate(); } void LateUpdate() { if (UpdateMethod != UpdateMethods.ManualUpdate) ManualUpdate(); } // Instead of FixedUpdate() we have this, to ensure that it happens // after all physics updates have taken place IEnumerator AfterPhysics() { while (true) { // FixedUpdate can be called multiple times per frame yield return m_WaitForFixedUpdate; DoFixedUpdate(); } } #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. 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)); } } // We don't want this in runtime because it's only for debugging and it can generate garbage void OnGUI() { if (CinemachineDebug.OnGUIHandlers != null && Event.current.type != EventType.Layout) CinemachineDebug.OnGUIHandlers(this); } #if CINEMACHINE_UIELEMENTS void OnGuiHandler(CinemachineBrain brain) { if (!ShowDebugText) { if (m_DebugText != null) { m_DebugText.Dispose(); m_DebugText = null; } return; } if (ActiveVirtualCamera == null || brain != this) return; m_DebugText ??= new DebugText(OutputCamera); // Show the active camera and blend var sb = CinemachineDebug.SBFromPool(); sb.Length = 0; sb.Append("CM "); sb.Append(gameObject.name); sb.Append(": "); if (CinemachineCore.SoloCamera != null) { sb.Append("SOLO "); m_DebugText.SetTextColor(CinemachineCore.SoloGUIColor()); } else m_DebugText.RestoreOriginalTextColor(); if (IsBlending) sb.Append(ActiveBlend.Description); else { var vcam = ActiveVirtualCamera; if (vcam == null) sb.Append("(none)"); else { sb.Append(vcam.Name); var desc = vcam.Description; if (!string.IsNullOrEmpty(desc)) { sb.Append(" "); sb.Append(desc); } } } m_DebugText.SetText(sb.ToString()); CinemachineDebug.ReturnToPool(sb); } #endif #endif // ============ ICameraOverrideStack implementation ================ /// <inheritdoc /> public int SetCameraOverride( int overrideId, int priority, ICinemachineCamera camA, ICinemachineCamera camB, float weightB, float deltaTime) => m_BlendManager.SetCameraOverride(overrideId, priority, camA, camB, weightB, deltaTime); /// <inheritdoc /> public void ReleaseCameraOverride(int overrideId) => m_BlendManager.ReleaseCameraOverride(overrideId); /// <summary>Get the default world up for the virtual cameras.</summary> public Vector3 DefaultWorldUp => (WorldUpOverride != null) ? WorldUpOverride.transform.up : Vector3.up; // ============ ICinemachineMixer implementation ================ /// <inheritdoc /> public string Name => name; /// <inheritdoc /> public string Description { get { if (ActiveVirtualCamera == null) return "(none)"; if (IsBlending) return ActiveBlend.Description; var sb = CinemachineDebug.SBFromPool(); sb.Append(ActiveVirtualCamera.Name); sb.Append(" "); sb.Append(ActiveVirtualCamera.Description); var text = sb.ToString(); CinemachineDebug.ReturnToPool(sb); return text; } } /// <inheritdoc /> public CameraState State => m_CameraState; /// <inheritdoc /> public bool IsValid => this != null; /// <inheritdoc /> public ICinemachineMixer ParentCamera => null; // GML todo: think about this /// <inheritdoc /> public void UpdateCameraState(Vector3 up, float deltaTime) {} // GML todo: think about this /// <inheritdoc /> public void OnCameraActivated(ICinemachineCamera.ActivationEventParams evt) {} // GML todo: think about this /// <inheritdoc /> public bool IsLiveChild(ICinemachineCamera cam, bool dominantChildOnly = false) { if (CinemachineCore.SoloCamera == cam || m_BlendManager.IsLive(cam)) return true; // Walk up the parents var parent = cam.ParentCamera; if (parent != null && parent.IsLiveChild(cam, dominantChildOnly)) return IsLiveChild(parent, dominantChildOnly); return false; } // ============ Global Brain cache ================ /// <summary>Access the array of active CinemachineBrains in the scene</summary> public static int ActiveBrainCount => s_ActiveBrains.Count; /// <summary>Access the array of active CinemachineBrains in the scene /// without generating garbage</summary> /// <param name="index">Index of the brain to access, range 0-ActiveBrainCount</param> /// <returns>The brain at the specified index</returns> public static CinemachineBrain GetActiveBrain(int index) => s_ActiveBrains[index]; // ============================ /// <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 } } } /// <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; } } /// <summary> /// Get the current active virtual camera. /// </summary> public ICinemachineCamera ActiveVirtualCamera => CinemachineCore.SoloCamera ?? m_BlendManager.ActiveVirtualCamera; /// <summary> /// Call this to reset the current active camera, causing the brain to choose a new /// one without blending. It is useful, for example, /// when you want to restart a game level. /// </summary> public void ResetState() => m_BlendManager.ResetRootFrame(); /// <summary> /// Is there a blend in progress? /// </summary> public bool IsBlending => m_BlendManager.IsBlending; /// <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 /// unless it is to set the active blend to null, which will force completion of the blend. /// </summary> public CinemachineBlend ActiveBlend { get => m_BlendManager.ActiveBlend; set => m_BlendManager.ActiveBlend = value; } /// <summary>Returns true if camera is on a channel that is handles by this Brain.</summary> /// <param name="vcam">The camera to check</param> /// <returns></returns> public bool IsValidChannel(CinemachineVirtualCameraBase vcam) => vcam != null && ((uint)vcam.OutputChannel & (uint)ChannelMask) != 0; /// <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="cam">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 cam) { if (m_BlendManager.IsLiveInBlend(cam)) return true; // Walk up the parents var parent = cam.ParentCamera; if (parent != null && parent.IsLiveChild(cam, false)) return IsLiveInBlend(parent); return false; } /// <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 || BlendUpdateMethod != BrainUpdateMethods.FixedUpdate) m_BlendManager.UpdateRootFrame(this, TopCameraFromPriorityQueue(), DefaultWorldUp, deltaTime); m_BlendManager.ComputeCurrentBlend(); if (Application.isPlaying && UpdateMethod == UpdateMethods.FixedUpdate) { // Special handling for fixed update: cameras that have been enabled // since the last physics frame must be updated now if (BlendUpdateMethod != BrainUpdateMethods.FixedUpdate) { CameraUpdateManager.s_CurrentUpdateFilter = CameraUpdateManager.UpdateFilter.Fixed; if (CinemachineCore.SoloCamera == null) m_BlendManager.RefreshCurrentCameraState(DefaultWorldUp, GetEffectiveDeltaTime(true)); } } else { var filter = CameraUpdateManager.UpdateFilter.Late; if (UpdateMethod == UpdateMethods.SmartUpdate) { // Track the targets UpdateTracker.OnUpdate(UpdateTracker.UpdateClock.Late); filter = CameraUpdateManager.UpdateFilter.SmartLate; } UpdateVirtualCameras(filter, deltaTime); } // Choose the active vcam and apply it to the Unity camera if (!Application.isPlaying || BlendUpdateMethod != BrainUpdateMethods.FixedUpdate) ProcessActiveCamera(deltaTime); } /// Called in the place of FixedUpdate void DoFixedUpdate() { if (UpdateMethod == UpdateMethods.FixedUpdate || UpdateMethod == UpdateMethods.SmartUpdate) { var filter = CameraUpdateManager.UpdateFilter.Fixed; if (UpdateMethod == UpdateMethods.SmartUpdate) { // Track the targets UpdateTracker.OnUpdate(UpdateTracker.UpdateClock.Fixed); filter = CameraUpdateManager.UpdateFilter.SmartFixed; } UpdateVirtualCameras(filter, GetEffectiveDeltaTime(true)); } // Choose the active vcam and apply it to the Unity camera if (BlendUpdateMethod == BrainUpdateMethods.FixedUpdate) { m_BlendManager.UpdateRootFrame(this, TopCameraFromPriorityQueue(), DefaultWorldUp, Time.fixedDeltaTime); ProcessActiveCamera(Time.fixedDeltaTime); } } float GetEffectiveDeltaTime(bool fixedDelta) { if (CinemachineCore.UniformDeltaTimeOverride >= 0) return CinemachineCore.UniformDeltaTimeOverride; if (CinemachineCore.SoloCamera != null) return Time.unscaledDeltaTime; if (!Application.isPlaying) return m_BlendManager.GetDeltaTimeOverride(); if (IgnoreTimeScale) return fixedDelta ? Time.fixedDeltaTime : Time.unscaledDeltaTime; return fixedDelta ? Time.fixedDeltaTime : Time.deltaTime; } void UpdateVirtualCameras(CameraUpdateManager.UpdateFilter updateFilter, float deltaTime) { // We always update all active virtual cameras CameraUpdateManager.s_CurrentUpdateFilter = updateFilter; CameraUpdateManager.UpdateAllActiveVirtualCameras((uint)ChannelMask, DefaultWorldUp, deltaTime); // Make sure all live cameras get updated, in case some of them are deactivated if (CinemachineCore.SoloCamera != null) CinemachineCore.SoloCamera.UpdateCameraState(DefaultWorldUp, deltaTime); m_BlendManager.RefreshCurrentCameraState(DefaultWorldUp, deltaTime); // Restore the filter for general use updateFilter = CameraUpdateManager.UpdateFilter.Late; if (Application.isPlaying) { if (UpdateMethod == UpdateMethods.SmartUpdate) updateFilter |= CameraUpdateManager.UpdateFilter.Smart; else if (UpdateMethod == UpdateMethods.FixedUpdate) updateFilter = CameraUpdateManager.UpdateFilter.Fixed; } CameraUpdateManager.s_CurrentUpdateFilter = updateFilter; } /// <summary> /// Choose the default active camera in the case that there is no camera override. /// </summary> /// <returns>The highest-priority Enabled ICinemachineCamera that is in my Channel Mask.</returns> protected virtual ICinemachineCamera TopCameraFromPriorityQueue() { int numCameras = CameraUpdateManager.VirtualCameraCount; for (int i = 0; i < numCameras; ++i) { var cam = CameraUpdateManager.GetVirtualCamera(i); if (IsValidChannel(cam)) return cam; } return null; } CinemachineBlendDefinition LookupBlend(ICinemachineCamera fromKey, ICinemachineCamera toKey) => CinemachineBlenderSettings.LookupBlend(fromKey, toKey, DefaultBlend, CustomBlends, this); void ProcessActiveCamera(float deltaTime) { if (CinemachineCore.SoloCamera != null) { var state = CinemachineCore.SoloCamera.State; PushStateToUnityCamera(ref state); } else if (m_BlendManager.ProcessActiveCamera(this, DefaultWorldUp, deltaTime) != null) { // Apply the vcam state to the Unity camera var state = m_BlendManager.CameraState; PushStateToUnityCamera(ref state); } else { // No active virtual 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.BlendHints.NoTransform | CameraState.BlendHints.NoLens; PushStateToUnityCamera(ref state); } } /// <summary> Apply a cref="CameraState"/> to the game object</summary> void PushStateToUnityCamera(ref CameraState state) { m_CameraState = state; var target = ControlledObject.transform; var pos = target.position; var rot = target.rotation; if ((state.BlendHint & CameraState.BlendHints.NoPosition) == 0) pos = state.GetFinalPosition(); if ((state.BlendHint & CameraState.BlendHints.NoOrientation) == 0) rot = state.GetFinalOrientation(); target.ConservativeSetPositionAndRotation(pos, rot); if ((state.BlendHint & CameraState.BlendHints.NoLens) == 0) { Camera cam = OutputCamera; if (cam != null) { bool isPhysical = cam.usePhysicalProperties; #if RESET_PROJECTION_MATRIX cam.ResetProjectionMatrix(); #endif cam.nearClipPlane = state.Lens.NearClipPlane; cam.farClipPlane = state.Lens.FarClipPlane; cam.orthographicSize = state.Lens.OrthographicSize; cam.fieldOfView = state.Lens.FieldOfView; #if RESET_PROJECTION_MATRIX if (!LensModeOverride.Enabled) cam.usePhysicalProperties = isPhysical; // because ResetProjectionMatrix resets it else #else if (LensModeOverride.Enabled) #endif { if (state.Lens.ModeOverride != LensSettings.OverrideModes.None) { isPhysical = state.Lens.IsPhysicalCamera; cam.orthographic = state.Lens.ModeOverride == LensSettings.OverrideModes.Orthographic; } else if (LensModeOverride.DefaultMode != LensSettings.OverrideModes.None) { isPhysical = LensModeOverride.DefaultMode == LensSettings.OverrideModes.Physical; cam.orthographic = LensModeOverride.DefaultMode == LensSettings.OverrideModes.Orthographic; } cam.usePhysicalProperties = isPhysical; } if (isPhysical) { cam.sensorSize = state.Lens.PhysicalProperties.SensorSize; cam.gateFit = state.Lens.PhysicalProperties.GateFit; cam.focalLength = Camera.FieldOfViewToFocalLength( state.Lens.FieldOfView, state.Lens.PhysicalProperties.SensorSize.y); cam.lensShift = state.Lens.PhysicalProperties.LensShift; cam.focusDistance = state.Lens.PhysicalProperties.FocusDistance; cam.iso = state.Lens.PhysicalProperties.Iso; cam.shutterSpeed = state.Lens.PhysicalProperties.ShutterSpeed; cam.aperture = state.Lens.PhysicalProperties.Aperture; cam.bladeCount = state.Lens.PhysicalProperties.BladeCount; cam.curvature = state.Lens.PhysicalProperties.Curvature; cam.barrelClipping = state.Lens.PhysicalProperties.BarrelClipping; cam.anamorphism = state.Lens.PhysicalProperties.Anamorphism; } } } // Send the camera updated event CinemachineCore.CameraUpdatedEvent.Invoke(this); } } }