CinemachineBrain.cs 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030
  1. #if !UNITY_2019_3_OR_NEWER
  2. #define CINEMACHINE_UNITY_IMGUI
  3. #endif
  4. using Cinemachine.Utility;
  5. using System;
  6. using System.Collections;
  7. using System.Collections.Generic;
  8. using UnityEngine;
  9. using UnityEngine.Events;
  10. using UnityEngine.SceneManagement;
  11. #if CINEMACHINE_HDRP || CINEMACHINE_URP
  12. #if CINEMACHINE_HDRP_7_3_1
  13. using UnityEngine.Rendering.HighDefinition;
  14. #else
  15. #if CINEMACHINE_URP
  16. using UnityEngine.Rendering.Universal;
  17. #else
  18. using UnityEngine.Experimental.Rendering.HDPipeline;
  19. #endif
  20. #endif
  21. #endif
  22. namespace Cinemachine
  23. {
  24. /// <summary>
  25. /// This interface is specifically for Timeline. Do not use it.
  26. /// </summary>
  27. public interface ICameraOverrideStack
  28. {
  29. /// <summary>
  30. /// Override the current camera and current blend. This setting will trump
  31. /// any in-game logic that sets virtual camera priorities and Enabled states.
  32. /// This is the main API for the timeline.
  33. /// </summary>
  34. /// <param name="overrideId">Id to represent a specific client. An internal
  35. /// stack is maintained, with the most recent non-empty override taking precenence.
  36. /// This id must be > 0. If you pass -1, a new id will be created, and returned.
  37. /// Use that id for subsequent calls. Don't forget to
  38. /// call ReleaseCameraOverride after all overriding is finished, to
  39. /// free the OverideStack resources.</param>
  40. /// <param name="camA">The camera to set, corresponding to weight=0.</param>
  41. /// <param name="camB">The camera to set, corresponding to weight=1.</param>
  42. /// <param name="weightB">The blend weight. 0=camA, 1=camB.</param>
  43. /// <param name="deltaTime">Override for deltaTime. Should be Time.FixedDelta for
  44. /// time-based calculations to be included, -1 otherwise.</param>
  45. /// <returns>The override ID. Don't forget to call ReleaseCameraOverride
  46. /// after all overriding is finished, to free the OverideStack resources.</returns>
  47. int SetCameraOverride(
  48. int overrideId,
  49. ICinemachineCamera camA, ICinemachineCamera camB,
  50. float weightB, float deltaTime);
  51. /// <summary>
  52. /// See SetCameraOverride. Call ReleaseCameraOverride after all overriding
  53. /// is finished, to free the OverrideStack resources.
  54. /// </summary>
  55. /// <param name="overrideId">The ID to released. This is the value that
  56. /// was returned by SetCameraOverride</param>
  57. void ReleaseCameraOverride(int overrideId);
  58. /// <summary>
  59. /// Get the current definition of Up. May be different from Vector3.up.
  60. /// </summary>
  61. Vector3 DefaultWorldUp { get; }
  62. }
  63. /// <summary>
  64. /// CinemachineBrain is the link between the Unity Camera and the Cinemachine Virtual
  65. /// Cameras in the scene. It monitors the priority stack to choose the current
  66. /// Virtual Camera, and blend with another if necessary. Finally and most importantly,
  67. /// it applies the Virtual Camera state to the attached Unity Camera.
  68. ///
  69. /// The CinemachineBrain is also the place where rules for blending between virtual cameras
  70. /// are defined. Camera blending is an interpolation over time of one virtual camera
  71. /// position and state to another. If you think of virtual cameras as cameramen, then
  72. /// blending is a little like one cameraman smoothly passing the camera to another cameraman.
  73. /// You can specify the time over which to blend, as well as the blend curve shape.
  74. /// Note that a camera cut is just a zero-time blend.
  75. /// </summary>
  76. [DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)]
  77. // [RequireComponent(typeof(Camera))] // strange but true: we can live without it
  78. [DisallowMultipleComponent]
  79. [ExecuteAlways]
  80. [AddComponentMenu("Cinemachine/CinemachineBrain")]
  81. [SaveDuringPlay]
  82. [HelpURL(Documentation.BaseURL + "manual/CinemachineBrainProperties.html")]
  83. public class CinemachineBrain : MonoBehaviour, ICameraOverrideStack
  84. {
  85. /// <summary>
  86. /// When enabled, the current camera and blend will be indicated in the
  87. /// game window, for debugging.
  88. /// </summary>
  89. [Tooltip("When enabled, the current camera and blend will be indicated in "
  90. + "the game window, for debugging")]
  91. public bool m_ShowDebugText = false;
  92. /// <summary>
  93. /// When enabled, shows the camera's frustum in the scene view.
  94. /// </summary>
  95. [Tooltip("When enabled, the camera's frustum will be shown at all times "
  96. + "in the scene view")]
  97. public bool m_ShowCameraFrustum = true;
  98. /// <summary>
  99. /// When enabled, the cameras will always respond in real-time to user input and damping,
  100. /// even if the game is running in slow motion
  101. /// </summary>
  102. [Tooltip("When enabled, the cameras will always respond in real-time to user input "
  103. + "and damping, even if the game is running in slow motion")]
  104. public bool m_IgnoreTimeScale = false;
  105. /// <summary>
  106. /// If set, this object's Y axis will define the worldspace Up vector for all the
  107. /// virtual cameras. This is useful in top-down game environments. If not set, Up is worldspace Y.
  108. /// </summary>
  109. [Tooltip("If set, this object's Y axis will define the worldspace Up vector for all the "
  110. + "virtual cameras. This is useful for instance in top-down game environments. "
  111. + "If not set, Up is worldspace Y. Setting this appropriately is important, "
  112. + "because Virtual Cameras don't like looking straight up or straight down.")]
  113. public Transform m_WorldUpOverride;
  114. /// <summary>This enum defines the options available for the update method.</summary>
  115. [DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)]
  116. public enum UpdateMethod
  117. {
  118. /// <summary>Virtual cameras are updated in sync with the Physics module, in FixedUpdate</summary>
  119. FixedUpdate,
  120. /// <summary>Virtual cameras are updated in MonoBehaviour LateUpdate.</summary>
  121. LateUpdate,
  122. /// <summary>Virtual cameras are updated according to how the target is updated.</summary>
  123. SmartUpdate,
  124. /// <summary>Virtual cameras are not automatically updated, client must explicitly call
  125. /// the CinemachineBrain's ManualUpdate() method.</summary>
  126. ManualUpdate
  127. };
  128. /// <summary>Depending on how the target objects are animated, adjust the update method to
  129. /// minimize the potential jitter. Use FixedUpdate if all your targets are animated with for RigidBody animation.
  130. /// SmartUpdate will choose the best method for each virtual camera, depending
  131. /// on how the target is animated.</summary>
  132. [Tooltip("The update time for the vcams. Use FixedUpdate if all your targets are animated "
  133. + "during FixedUpdate (e.g. RigidBodies), LateUpdate if all your targets are animated "
  134. + "during the normal Update loop, and SmartUpdate if you want Cinemachine to do the "
  135. + "appropriate thing on a per-target basis. SmartUpdate is the recommended setting")]
  136. public UpdateMethod m_UpdateMethod = UpdateMethod.SmartUpdate;
  137. /// <summary>This enum defines the options available for the update method.</summary>
  138. [DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)]
  139. public enum BrainUpdateMethod
  140. {
  141. /// <summary>Camera is updated in sync with the Physics module, in FixedUpdate</summary>
  142. FixedUpdate,
  143. /// <summary>Camera is updated in MonoBehaviour LateUpdate (or when ManualUpdate is called).</summary>
  144. LateUpdate
  145. };
  146. /// <summary>The update time for the Brain, i.e. when the blends are evaluated and the
  147. /// brain's transform is updated.</summary>
  148. [Tooltip("The update time for the Brain, i.e. when the blends are evaluated and "
  149. + "the brain's transform is updated")]
  150. public BrainUpdateMethod m_BlendUpdateMethod = BrainUpdateMethod.LateUpdate;
  151. /// <summary>
  152. /// The blend which is used if you don't explicitly define a blend between two Virtual Cameras.
  153. /// </summary>
  154. [CinemachineBlendDefinitionProperty]
  155. [Tooltip("The blend that is used in cases where you haven't explicitly defined a "
  156. + "blend between two Virtual Cameras")]
  157. public CinemachineBlendDefinition m_DefaultBlend
  158. = new CinemachineBlendDefinition(CinemachineBlendDefinition.Style.EaseInOut, 2f);
  159. /// <summary>
  160. /// This is the asset which contains custom settings for specific blends.
  161. /// </summary>
  162. [Tooltip("This is the asset that contains custom settings for blends between "
  163. + "specific virtual cameras in your scene")]
  164. public CinemachineBlenderSettings m_CustomBlends = null;
  165. /// <summary>
  166. /// Get the Unity Camera that is attached to this GameObject. This is the camera
  167. /// that will be controlled by the brain.
  168. /// </summary>
  169. public Camera OutputCamera
  170. {
  171. get
  172. {
  173. if (m_OutputCamera == null && !Application.isPlaying)
  174. ControlledObject.TryGetComponent(out m_OutputCamera);
  175. return m_OutputCamera;
  176. }
  177. }
  178. private Camera m_OutputCamera = null; // never use directly - use accessor
  179. /// <summary>
  180. /// CinemachineBrain controls this GameObject. Normally, this is the GameObject to which
  181. /// the CinemachineBrain component is attached. However, it is possible to override this
  182. /// by setting this property to another GameObject. If a Camera component is attached to the
  183. /// Controlled Object, then that Camera component's lens settings will also be driven
  184. /// by the CinemachineBrain.
  185. /// If this property is set to null, then CinemachineBrain is controlling the GameObject
  186. /// to which it is attached. The value of this property will always report as non-null.
  187. /// </summary>
  188. public GameObject ControlledObject
  189. {
  190. get => m_TargetOverride == null ? gameObject : m_TargetOverride;
  191. set
  192. {
  193. if (!ReferenceEquals(m_TargetOverride, value))
  194. {
  195. m_TargetOverride = value;
  196. ControlledObject.TryGetComponent(out m_OutputCamera); // update output camera when target changes
  197. }
  198. }
  199. }
  200. private GameObject m_TargetOverride = null; // never use directly - use accessor
  201. /// <summary>Event with a CinemachineBrain parameter</summary>
  202. [Serializable] public class BrainEvent : UnityEvent<CinemachineBrain> {}
  203. /// <summary>
  204. /// Event that is fired when a virtual camera is activated.
  205. /// The parameters are (incoming_vcam, outgoing_vcam), in that order.
  206. /// </summary>
  207. [Serializable] public class VcamActivatedEvent : UnityEvent<ICinemachineCamera, ICinemachineCamera> {}
  208. /// <summary>This event will fire whenever a virtual camera goes live and there is no blend</summary>
  209. [Tooltip("This event will fire whenever a virtual camera goes live and there is no blend")]
  210. public BrainEvent m_CameraCutEvent = new BrainEvent();
  211. /// <summary>This event will fire whenever a virtual camera goes live. If a blend is involved,
  212. /// then the event will fire on the first frame of the blend.
  213. ///
  214. /// The Parameters are (incoming_vcam, outgoing_vcam), in that order.</summary>
  215. [Tooltip("This event will fire whenever a virtual camera goes live. If a blend is "
  216. + "involved, then the event will fire on the first frame of the blend.")]
  217. public VcamActivatedEvent m_CameraActivatedEvent = new VcamActivatedEvent();
  218. /// <summary>
  219. /// API for the Unity Editor.
  220. /// Show this camera no matter what. This is static, and so affects all Cinemachine brains.
  221. /// </summary>
  222. public static ICinemachineCamera SoloCamera
  223. {
  224. get { return mSoloCamera; }
  225. set
  226. {
  227. if (value != null && !CinemachineCore.Instance.IsLive(value))
  228. value.OnTransitionFromCamera(null, Vector3.up, CinemachineCore.DeltaTime);
  229. mSoloCamera = value;
  230. }
  231. }
  232. /// <summary>API for the Unity Editor.</summary>
  233. /// <returns>Color used to indicate that a camera is in Solo mode.</returns>
  234. public static Color GetSoloGUIColor() { return Color.Lerp(Color.red, Color.yellow, 0.8f); }
  235. /// <summary>Get the default world up for the virtual cameras.</summary>
  236. public Vector3 DefaultWorldUp
  237. { get { return (m_WorldUpOverride != null) ? m_WorldUpOverride.transform.up : Vector3.up; } }
  238. private static ICinemachineCamera mSoloCamera;
  239. private Coroutine mPhysicsCoroutine;
  240. private int m_LastFrameUpdated;
  241. private void OnEnable()
  242. {
  243. // Make sure there is a first stack frame
  244. if (mFrameStack.Count == 0)
  245. mFrameStack.Add(new BrainFrame());
  246. CinemachineCore.Instance.AddActiveBrain(this);
  247. CinemachineDebug.OnGUIHandlers -= OnGuiHandler;
  248. CinemachineDebug.OnGUIHandlers += OnGuiHandler;
  249. // We check in after the physics system has had a chance to move things
  250. mPhysicsCoroutine = StartCoroutine(AfterPhysics());
  251. SceneManager.sceneLoaded += OnSceneLoaded;
  252. SceneManager.sceneUnloaded += OnSceneUnloaded;
  253. }
  254. private void OnDisable()
  255. {
  256. SceneManager.sceneLoaded -= OnSceneLoaded;
  257. SceneManager.sceneUnloaded -= OnSceneUnloaded;
  258. CinemachineDebug.OnGUIHandlers -= OnGuiHandler;
  259. CinemachineCore.Instance.RemoveActiveBrain(this);
  260. mFrameStack.Clear();
  261. StopCoroutine(mPhysicsCoroutine);
  262. }
  263. void OnSceneLoaded(Scene scene, LoadSceneMode mode)
  264. {
  265. if (Time.frameCount == m_LastFrameUpdated && mFrameStack.Count > 0)
  266. ManualUpdate();
  267. }
  268. void OnSceneUnloaded(Scene scene)
  269. {
  270. if (Time.frameCount == m_LastFrameUpdated && mFrameStack.Count > 0)
  271. ManualUpdate();
  272. }
  273. void Awake()
  274. {
  275. ControlledObject.TryGetComponent(out m_OutputCamera);
  276. }
  277. void Start()
  278. {
  279. m_LastFrameUpdated = -1;
  280. UpdateVirtualCameras(CinemachineCore.UpdateFilter.Late, -1f);
  281. }
  282. private void OnGuiHandler()
  283. {
  284. #if CINEMACHINE_UNITY_IMGUI
  285. if (!m_ShowDebugText)
  286. CinemachineDebug.ReleaseScreenPos(this);
  287. else
  288. {
  289. // Show the active camera and blend
  290. var sb = CinemachineDebug.SBFromPool();
  291. Color color = GUI.color;
  292. sb.Length = 0;
  293. sb.Append("CM ");
  294. sb.Append(gameObject.name);
  295. sb.Append(": ");
  296. if (SoloCamera != null)
  297. {
  298. sb.Append("SOLO ");
  299. GUI.color = GetSoloGUIColor();
  300. }
  301. if (IsBlending)
  302. sb.Append(ActiveBlend.Description);
  303. else
  304. {
  305. ICinemachineCamera vcam = ActiveVirtualCamera;
  306. if (vcam == null)
  307. sb.Append("(none)");
  308. else
  309. {
  310. sb.Append("[");
  311. sb.Append(vcam.Name);
  312. sb.Append("]");
  313. }
  314. }
  315. string text = sb.ToString();
  316. Rect r = CinemachineDebug.GetScreenPos(this, text, GUI.skin.box);
  317. GUI.Label(r, text, GUI.skin.box);
  318. GUI.color = color;
  319. CinemachineDebug.ReturnToPool(sb);
  320. }
  321. #endif
  322. }
  323. #if UNITY_EDITOR
  324. private void OnGUI()
  325. {
  326. if (CinemachineDebug.OnGUIHandlers != null)
  327. CinemachineDebug.OnGUIHandlers();
  328. }
  329. #endif
  330. WaitForFixedUpdate mWaitForFixedUpdate = new WaitForFixedUpdate();
  331. private IEnumerator AfterPhysics()
  332. {
  333. while (true)
  334. {
  335. // FixedUpdate can be called multiple times per frame
  336. yield return mWaitForFixedUpdate;
  337. if (m_UpdateMethod == UpdateMethod.FixedUpdate
  338. || m_UpdateMethod == UpdateMethod.SmartUpdate)
  339. {
  340. CinemachineCore.UpdateFilter filter = CinemachineCore.UpdateFilter.Fixed;
  341. if (m_UpdateMethod == UpdateMethod.SmartUpdate)
  342. {
  343. // Track the targets
  344. UpdateTracker.OnUpdate(UpdateTracker.UpdateClock.Fixed);
  345. filter = CinemachineCore.UpdateFilter.SmartFixed;
  346. }
  347. UpdateVirtualCameras(filter, GetEffectiveDeltaTime(true));
  348. }
  349. // Choose the active vcam and apply it to the Unity camera
  350. if (m_BlendUpdateMethod == BrainUpdateMethod.FixedUpdate)
  351. {
  352. UpdateFrame0(Time.fixedDeltaTime);
  353. ProcessActiveCamera(Time.fixedDeltaTime);
  354. }
  355. }
  356. }
  357. private void LateUpdate()
  358. {
  359. if (m_UpdateMethod != UpdateMethod.ManualUpdate)
  360. ManualUpdate();
  361. }
  362. /// <summary>
  363. /// Call this method explicitly from an external script to update the virtual cameras
  364. /// and position the main camera, if the UpdateMode is set to ManualUpdate.
  365. /// For other update modes, this method is called automatically, and should not be
  366. /// called from elsewhere.
  367. /// </summary>
  368. public void ManualUpdate()
  369. {
  370. m_LastFrameUpdated = Time.frameCount;
  371. float deltaTime = GetEffectiveDeltaTime(false);
  372. if (!Application.isPlaying || m_BlendUpdateMethod != BrainUpdateMethod.FixedUpdate)
  373. UpdateFrame0(deltaTime);
  374. ComputeCurrentBlend(ref mCurrentLiveCameras, 0);
  375. if (Application.isPlaying && m_UpdateMethod == UpdateMethod.FixedUpdate)
  376. {
  377. // Special handling for fixed update: cameras that have been enabled
  378. // since the last physics frame must be updated now
  379. if (m_BlendUpdateMethod != BrainUpdateMethod.FixedUpdate)
  380. {
  381. CinemachineCore.Instance.m_CurrentUpdateFilter = CinemachineCore.UpdateFilter.Fixed;
  382. if (SoloCamera == null)
  383. mCurrentLiveCameras.UpdateCameraState(
  384. DefaultWorldUp, GetEffectiveDeltaTime(true));
  385. }
  386. }
  387. else
  388. {
  389. CinemachineCore.UpdateFilter filter = CinemachineCore.UpdateFilter.Late;
  390. if (m_UpdateMethod == UpdateMethod.SmartUpdate)
  391. {
  392. // Track the targets
  393. UpdateTracker.OnUpdate(UpdateTracker.UpdateClock.Late);
  394. filter = CinemachineCore.UpdateFilter.SmartLate;
  395. }
  396. UpdateVirtualCameras(filter, deltaTime);
  397. }
  398. // Choose the active vcam and apply it to the Unity camera
  399. if (!Application.isPlaying || m_BlendUpdateMethod != BrainUpdateMethod.FixedUpdate)
  400. ProcessActiveCamera(deltaTime);
  401. }
  402. #if UNITY_EDITOR
  403. /// This is only needed in editor mode to force timeline to call OnGUI while
  404. /// timeline is up and the game is not running, in order to allow dragging
  405. /// the composer guide in the game view.
  406. private void OnPreCull()
  407. {
  408. if (!Application.isPlaying)
  409. {
  410. // Note: this call will cause any screen canvas attached to the camera
  411. // to be painted one frame out of sync. It will only happen in the editor when not playing.
  412. ProcessActiveCamera(GetEffectiveDeltaTime(false));
  413. }
  414. }
  415. #endif
  416. private float GetEffectiveDeltaTime(bool fixedDelta)
  417. {
  418. if (CinemachineCore.UniformDeltaTimeOverride >= 0)
  419. return CinemachineCore.UniformDeltaTimeOverride;
  420. if (SoloCamera != null)
  421. return Time.unscaledDeltaTime;
  422. if (!Application.isPlaying)
  423. {
  424. for (int i = mFrameStack.Count - 1; i > 0; --i)
  425. {
  426. var frame = mFrameStack[i];
  427. if (frame.Active)
  428. return frame.deltaTimeOverride;
  429. }
  430. return -1;
  431. }
  432. if (m_IgnoreTimeScale)
  433. return fixedDelta ? Time.fixedDeltaTime : Time.unscaledDeltaTime;
  434. return fixedDelta ? Time.fixedDeltaTime : Time.deltaTime;
  435. }
  436. private void UpdateVirtualCameras(CinemachineCore.UpdateFilter updateFilter, float deltaTime)
  437. {
  438. // We always update all active virtual cameras
  439. CinemachineCore.Instance.m_CurrentUpdateFilter = updateFilter;
  440. Camera camera = OutputCamera;
  441. CinemachineCore.Instance.UpdateAllActiveVirtualCameras(
  442. camera == null ? -1 : camera.cullingMask, DefaultWorldUp, deltaTime);
  443. // Make sure all live cameras get updated, in case some of them are deactivated
  444. if (SoloCamera != null)
  445. SoloCamera.UpdateCameraState(DefaultWorldUp, deltaTime);
  446. mCurrentLiveCameras.UpdateCameraState(DefaultWorldUp, deltaTime);
  447. // Restore the filter for general use
  448. updateFilter = CinemachineCore.UpdateFilter.Late;
  449. if (Application.isPlaying)
  450. {
  451. if (m_UpdateMethod == UpdateMethod.SmartUpdate)
  452. updateFilter |= CinemachineCore.UpdateFilter.Smart;
  453. else if (m_UpdateMethod == UpdateMethod.FixedUpdate)
  454. updateFilter = CinemachineCore.UpdateFilter.Fixed;
  455. }
  456. CinemachineCore.Instance.m_CurrentUpdateFilter = updateFilter;
  457. }
  458. /// <summary>
  459. /// Get the current active virtual camera.
  460. /// </summary>
  461. public ICinemachineCamera ActiveVirtualCamera
  462. {
  463. get
  464. {
  465. if (SoloCamera != null)
  466. return SoloCamera;
  467. return DeepCamBFromBlend(mCurrentLiveCameras);
  468. }
  469. }
  470. static ICinemachineCamera DeepCamBFromBlend(CinemachineBlend blend)
  471. {
  472. ICinemachineCamera vcam = blend.CamB;
  473. while (vcam != null)
  474. {
  475. if (!vcam.IsValid)
  476. return null; // deleted!
  477. BlendSourceVirtualCamera bs = vcam as BlendSourceVirtualCamera;
  478. if (bs == null)
  479. break;
  480. vcam = bs.Blend.CamB;
  481. }
  482. return vcam;
  483. }
  484. /// <summary>
  485. /// Checks if the vcam is live as part of an outgoing blend.
  486. /// Does not check whether the vcam is also the current active vcam.
  487. /// </summary>
  488. /// <param name="vcam">The virtual camera to check</param>
  489. /// <returns>True if the virtual camera is part of a live outgoing blend, false otherwise</returns>
  490. public bool IsLiveInBlend(ICinemachineCamera vcam)
  491. {
  492. // Ignore mCurrentLiveCameras.CamB
  493. if (vcam == mCurrentLiveCameras.CamA)
  494. return true;
  495. var b = mCurrentLiveCameras.CamA as BlendSourceVirtualCamera;
  496. if (b != null && b.Blend.Uses(vcam))
  497. return true;
  498. ICinemachineCamera parent = vcam.ParentCamera;
  499. if (parent != null && parent.IsLiveChild(vcam, false))
  500. return IsLiveInBlend(parent);
  501. return false;
  502. }
  503. /// <summary>
  504. /// Is there a blend in progress?
  505. /// </summary>
  506. public bool IsBlending { get { return ActiveBlend != null; } }
  507. /// <summary>
  508. /// Get the current blend in progress. Returns null if none.
  509. /// It is also possible to set the current blend, but this is not a recommended usage.
  510. /// </summary>
  511. public CinemachineBlend ActiveBlend
  512. {
  513. get
  514. {
  515. if (SoloCamera != null)
  516. return null;
  517. if (mCurrentLiveCameras.CamA == null || mCurrentLiveCameras.Equals(null) || mCurrentLiveCameras.IsComplete)
  518. return null;
  519. return mCurrentLiveCameras;
  520. }
  521. set
  522. {
  523. if (value == null)
  524. mFrameStack[0].blend.Duration = 0;
  525. else
  526. mFrameStack[0].blend = value;
  527. }
  528. }
  529. private class BrainFrame
  530. {
  531. public int id;
  532. public CinemachineBlend blend = new CinemachineBlend(null, null, null, 0, 0);
  533. public bool Active { get { return blend.IsValid; } }
  534. // Working data - updated every frame
  535. public CinemachineBlend workingBlend = new CinemachineBlend(null, null, null, 0, 0);
  536. public BlendSourceVirtualCamera workingBlendSource = new BlendSourceVirtualCamera(null);
  537. // Used by Timeline Preview for overriding the current value of deltaTime
  538. public float deltaTimeOverride;
  539. // Used for blend reversal. Range is 0...1,
  540. // representing where the blend started when reversed mid-blend
  541. public float blendStartPosition;
  542. }
  543. // Current game state is always frame 0, overrides are subsequent frames
  544. private List<BrainFrame> mFrameStack = new List<BrainFrame>();
  545. private int mNextFrameId = 1;
  546. /// Get the frame index corresponding to the ID
  547. private int GetBrainFrame(int withId)
  548. {
  549. int count = mFrameStack.Count;
  550. for (int i = count - 1; i > 0; --i)
  551. if (mFrameStack[i].id == withId)
  552. return i;
  553. // Not found - add it
  554. mFrameStack.Add(new BrainFrame() { id = withId });
  555. return mFrameStack.Count - 1;
  556. }
  557. // Current Brain State - result of all frames. Blend camB is "current" camera always
  558. CinemachineBlend mCurrentLiveCameras = new CinemachineBlend(null, null, null, 0, 0);
  559. // To avoid GC memory alloc every frame
  560. private static readonly AnimationCurve mDefaultLinearAnimationCurve = AnimationCurve.Linear(0, 0, 1, 1);
  561. /// <summary>
  562. /// This API is specifically for Timeline. Do not use it.
  563. /// Override the current camera and current blend. This setting will trump
  564. /// any in-game logic that sets virtual camera priorities and Enabled states.
  565. /// This is the main API for the timeline.
  566. /// </summary>
  567. /// <param name="overrideId">Id to represent a specific client. An internal
  568. /// stack is maintained, with the most recent non-empty override taking precenence.
  569. /// This id must be > 0. If you pass -1, a new id will be created, and returned.
  570. /// Use that id for subsequent calls. Don't forget to
  571. /// call ReleaseCameraOverride after all overriding is finished, to
  572. /// free the OverideStack resources.</param>
  573. /// <param name="camA"> The camera to set, corresponding to weight=0</param>
  574. /// <param name="camB"> The camera to set, corresponding to weight=1</param>
  575. /// <param name="weightB">The blend weight. 0=camA, 1=camB</param>
  576. /// <param name="deltaTime">override for deltaTime. Should be Time.FixedDelta for
  577. /// time-based calculations to be included, -1 otherwise</param>
  578. /// <returns>The oiverride ID. Don't forget to call ReleaseCameraOverride
  579. /// after all overriding is finished, to free the OverideStack resources.</returns>
  580. public int SetCameraOverride(
  581. int overrideId,
  582. ICinemachineCamera camA, ICinemachineCamera camB,
  583. float weightB, float deltaTime)
  584. {
  585. if (overrideId < 0)
  586. overrideId = mNextFrameId++;
  587. BrainFrame frame = mFrameStack[GetBrainFrame(overrideId)];
  588. frame.deltaTimeOverride = deltaTime;
  589. frame.blend.CamA = camA;
  590. frame.blend.CamB = camB;
  591. frame.blend.BlendCurve = mDefaultLinearAnimationCurve;
  592. frame.blend.Duration = 1;
  593. frame.blend.TimeInBlend = weightB;
  594. // In case vcams are inactive game objects, make sure they get initialized properly
  595. var cam = camA as CinemachineVirtualCameraBase;
  596. if (cam != null)
  597. cam.EnsureStarted();
  598. cam = camB as CinemachineVirtualCameraBase;
  599. if (cam != null)
  600. cam.EnsureStarted();
  601. return overrideId;
  602. }
  603. /// <summary>
  604. /// This API is specifically for Timeline. Do not use it.
  605. /// Release the resources used for a camera override client.
  606. /// See SetCameraOverride.
  607. /// </summary>
  608. /// <param name="overrideId">The ID to released. This is the value that
  609. /// was returned by SetCameraOverride</param>
  610. public void ReleaseCameraOverride(int overrideId)
  611. {
  612. for (int i = mFrameStack.Count - 1; i > 0; --i)
  613. {
  614. if (mFrameStack[i].id == overrideId)
  615. {
  616. mFrameStack.RemoveAt(i);
  617. return;
  618. }
  619. }
  620. }
  621. ICinemachineCamera mActiveCameraPreviousFrame;
  622. GameObject mActiveCameraPreviousFrameGameObject;
  623. private void ProcessActiveCamera(float deltaTime)
  624. {
  625. var activeCamera = ActiveVirtualCamera;
  626. if (SoloCamera != null)
  627. {
  628. var state = SoloCamera.State;
  629. PushStateToUnityCamera(ref state);
  630. }
  631. else if (activeCamera == null)
  632. {
  633. // No active virtal camera. We create a state representing its position
  634. // and call the callback, but we don't actively set the transform or lens
  635. var state = CameraState.Default;
  636. var target = ControlledObject.transform;
  637. state.RawPosition = target.position;
  638. state.RawOrientation = target.rotation;
  639. state.Lens = LensSettings.FromCamera(m_OutputCamera);
  640. state.BlendHint |= CameraState.BlendHintValue.NoTransform | CameraState.BlendHintValue.NoLens;
  641. PushStateToUnityCamera(ref state);
  642. }
  643. else
  644. {
  645. // Has the current camera changed this frame?
  646. if (mActiveCameraPreviousFrameGameObject == null)
  647. mActiveCameraPreviousFrame = null; // object was deleted
  648. if (activeCamera != mActiveCameraPreviousFrame)
  649. {
  650. // Notify incoming camera of transition
  651. activeCamera.OnTransitionFromCamera(
  652. mActiveCameraPreviousFrame, DefaultWorldUp, deltaTime);
  653. if (m_CameraActivatedEvent != null)
  654. m_CameraActivatedEvent.Invoke(activeCamera, mActiveCameraPreviousFrame);
  655. // If we're cutting without a blend, send an event
  656. if (!IsBlending || (mActiveCameraPreviousFrame != null
  657. && !ActiveBlend.Uses(mActiveCameraPreviousFrame)))
  658. {
  659. if (m_CameraCutEvent != null)
  660. m_CameraCutEvent.Invoke(this);
  661. if (CinemachineCore.CameraCutEvent != null)
  662. CinemachineCore.CameraCutEvent.Invoke(this);
  663. }
  664. // Re-update in case it's inactive
  665. activeCamera.UpdateCameraState(DefaultWorldUp, deltaTime);
  666. }
  667. // Apply the vcam state to the Unity camera
  668. var state = mCurrentLiveCameras.State;
  669. PushStateToUnityCamera(ref state);
  670. }
  671. mActiveCameraPreviousFrame = activeCamera;
  672. mActiveCameraPreviousFrameGameObject
  673. = activeCamera == null ? null : activeCamera.VirtualCameraGameObject;
  674. }
  675. private void UpdateFrame0(float deltaTime)
  676. {
  677. // Make sure there is a first stack frame
  678. if (mFrameStack.Count == 0)
  679. mFrameStack.Add(new BrainFrame());
  680. // Update the in-game frame (frame 0)
  681. BrainFrame frame = mFrameStack[0];
  682. // Are we transitioning cameras?
  683. var activeCamera = TopCameraFromPriorityQueue();
  684. var outGoingCamera = frame.blend.CamB;
  685. if (activeCamera != outGoingCamera)
  686. {
  687. // Do we need to create a game-play blend?
  688. if ((UnityEngine.Object)activeCamera != null
  689. && (UnityEngine.Object)outGoingCamera != null && deltaTime >= 0)
  690. {
  691. // Create a blend (curve will be null if a cut)
  692. var blendDef = LookupBlend(outGoingCamera, activeCamera);
  693. float blendDuration = blendDef.BlendTime;
  694. float blendStartPosition = 0;
  695. if (blendDef.BlendCurve != null && blendDuration > UnityVectorExtensions.Epsilon)
  696. {
  697. if (frame.blend.IsComplete)
  698. frame.blend.CamA = outGoingCamera; // new blend
  699. else
  700. {
  701. // Special case: if backing out of a blend-in-progress
  702. // with the same blend in reverse, adjust the blend time
  703. // to cancel out the progress made in the opposite direction
  704. if ((frame.blend.CamA == activeCamera
  705. || (frame.blend.CamA as BlendSourceVirtualCamera)?.Blend.CamB == activeCamera)
  706. && frame.blend.CamB == outGoingCamera)
  707. {
  708. // How far have we blended? That is what we must undo
  709. var progress = frame.blendStartPosition
  710. + (1 - frame.blendStartPosition) * frame.blend.TimeInBlend / frame.blend.Duration;
  711. blendDuration *= progress;
  712. blendStartPosition = 1 - progress;
  713. }
  714. // Chain to existing blend
  715. frame.blend.CamA = new BlendSourceVirtualCamera(
  716. new CinemachineBlend(
  717. frame.blend.CamA, frame.blend.CamB,
  718. frame.blend.BlendCurve, frame.blend.Duration,
  719. frame.blend.TimeInBlend));
  720. }
  721. }
  722. frame.blend.BlendCurve = blendDef.BlendCurve;
  723. frame.blend.Duration = blendDuration;
  724. frame.blend.TimeInBlend = 0;
  725. frame.blendStartPosition = blendStartPosition;
  726. }
  727. // Set the current active camera
  728. frame.blend.CamB = activeCamera;
  729. }
  730. // Advance the current blend (if any)
  731. if (frame.blend.CamA != null)
  732. {
  733. frame.blend.TimeInBlend += (deltaTime >= 0) ? deltaTime : frame.blend.Duration;
  734. if (frame.blend.IsComplete)
  735. {
  736. // No more blend
  737. frame.blend.CamA = null;
  738. frame.blend.BlendCurve = null;
  739. frame.blend.Duration = 0;
  740. frame.blend.TimeInBlend = 0;
  741. }
  742. }
  743. }
  744. /// <summary>
  745. /// Used internally to compute the currrent blend, taking into account
  746. /// the in-game camera and all the active overrides. Caller may optionally
  747. /// exclude n topmost overrides.
  748. /// </summary>
  749. /// <param name="outputBlend">Receives the nested blend</param>
  750. /// <param name="numTopLayersToExclude">Optionaly exclude the last number
  751. /// of overrides from the blend</param>
  752. public void ComputeCurrentBlend(
  753. ref CinemachineBlend outputBlend, int numTopLayersToExclude)
  754. {
  755. // Make sure there is a first stack frame
  756. if (mFrameStack.Count == 0)
  757. mFrameStack.Add(new BrainFrame());
  758. // Resolve the current working frame states in the stack
  759. int lastActive = 0;
  760. int topLayer = Mathf.Max(1, mFrameStack.Count - numTopLayersToExclude);
  761. for (int i = 0; i < topLayer; ++i)
  762. {
  763. BrainFrame frame = mFrameStack[i];
  764. if (i == 0 || frame.Active)
  765. {
  766. frame.workingBlend.CamA = frame.blend.CamA;
  767. frame.workingBlend.CamB = frame.blend.CamB;
  768. frame.workingBlend.BlendCurve = frame.blend.BlendCurve;
  769. frame.workingBlend.Duration = frame.blend.Duration;
  770. frame.workingBlend.TimeInBlend = frame.blend.TimeInBlend;
  771. if (i > 0 && !frame.blend.IsComplete)
  772. {
  773. if (frame.workingBlend.CamA == null)
  774. {
  775. if (mFrameStack[lastActive].blend.IsComplete)
  776. frame.workingBlend.CamA = mFrameStack[lastActive].blend.CamB;
  777. else
  778. {
  779. frame.workingBlendSource.Blend = mFrameStack[lastActive].workingBlend;
  780. frame.workingBlend.CamA = frame.workingBlendSource;
  781. }
  782. }
  783. else if (frame.workingBlend.CamB == null)
  784. {
  785. if (mFrameStack[lastActive].blend.IsComplete)
  786. frame.workingBlend.CamB = mFrameStack[lastActive].blend.CamB;
  787. else
  788. {
  789. frame.workingBlendSource.Blend = mFrameStack[lastActive].workingBlend;
  790. frame.workingBlend.CamB = frame.workingBlendSource;
  791. }
  792. }
  793. }
  794. lastActive = i;
  795. }
  796. }
  797. var workingBlend = mFrameStack[lastActive].workingBlend;
  798. outputBlend.CamA = workingBlend.CamA;
  799. outputBlend.CamB = workingBlend.CamB;
  800. outputBlend.BlendCurve = workingBlend.BlendCurve;
  801. outputBlend.Duration = workingBlend.Duration;
  802. outputBlend.TimeInBlend = workingBlend.TimeInBlend;
  803. }
  804. /// <summary>
  805. /// True if the ICinemachineCamera the current active camera,
  806. /// or part of a current blend, either directly or indirectly because its parents are live.
  807. /// </summary>
  808. /// <param name="vcam">The camera to test whether it is live</param>
  809. /// <param name="dominantChildOnly">If true, will only return true if this vcam is the dominat live child</param>
  810. /// <returns>True if the camera is live (directly or indirectly)
  811. /// or part of a blend in progress.</returns>
  812. public bool IsLive(ICinemachineCamera vcam, bool dominantChildOnly = false)
  813. {
  814. if (SoloCamera == vcam)
  815. return true;
  816. if (mCurrentLiveCameras.Uses(vcam))
  817. return true;
  818. ICinemachineCamera parent = vcam.ParentCamera;
  819. while (parent != null && parent.IsLiveChild(vcam, dominantChildOnly))
  820. {
  821. if (SoloCamera == parent || mCurrentLiveCameras.Uses(parent))
  822. return true;
  823. vcam = parent;
  824. parent = vcam.ParentCamera;
  825. }
  826. return false;
  827. }
  828. /// <summary>
  829. /// The current state applied to the unity camera (may be the result of a blend)
  830. /// </summary>
  831. public CameraState CurrentCameraState { get; private set; }
  832. /// <summary>
  833. /// Get the highest-priority Enabled ICinemachineCamera
  834. /// that is visible to my camera. Culling Mask is used to test visibility.
  835. /// </summary>
  836. /// <returns>The highest-priority Enabled ICinemachineCamera that is in my visible layers.</returns>
  837. protected virtual ICinemachineCamera TopCameraFromPriorityQueue()
  838. {
  839. CinemachineCore core = CinemachineCore.Instance;
  840. Camera outputCamera = OutputCamera;
  841. int mask = outputCamera == null ? ~0 : outputCamera.cullingMask;
  842. int numCameras = core.VirtualCameraCount;
  843. for (int i = 0; i < numCameras; ++i)
  844. {
  845. var cam = core.GetVirtualCamera(i);
  846. GameObject go = cam != null ? cam.gameObject : null;
  847. if (go != null && (mask & (1 << go.layer)) != 0)
  848. return cam;
  849. }
  850. return null;
  851. }
  852. /// <summary>
  853. /// Create a blend curve for blending from one ICinemachineCamera to another.
  854. /// If there is a specific blend defined for these cameras it will be used, otherwise
  855. /// a default blend will be created, which could be a cut.
  856. /// </summary>
  857. private CinemachineBlendDefinition LookupBlend(
  858. ICinemachineCamera fromKey, ICinemachineCamera toKey)
  859. {
  860. // Get the blend curve that's most appropriate for these cameras
  861. CinemachineBlendDefinition blend = m_DefaultBlend;
  862. if (m_CustomBlends != null)
  863. {
  864. string fromCameraName = (fromKey != null) ? fromKey.Name : string.Empty;
  865. string toCameraName = (toKey != null) ? toKey.Name : string.Empty;
  866. blend = m_CustomBlends.GetBlendForVirtualCameras(
  867. fromCameraName, toCameraName, blend);
  868. }
  869. if (CinemachineCore.GetBlendOverride != null)
  870. blend = CinemachineCore.GetBlendOverride(fromKey, toKey, blend, this);
  871. return blend;
  872. }
  873. /// <summary> Apply a cref="CameraState"/> to the game object</summary>
  874. private void PushStateToUnityCamera(ref CameraState state)
  875. {
  876. CurrentCameraState = state;
  877. var target = ControlledObject.transform;
  878. var pos = target.position;
  879. var rot = target.rotation;
  880. if ((state.BlendHint & CameraState.BlendHintValue.NoPosition) == 0)
  881. pos = state.FinalPosition;
  882. if ((state.BlendHint & CameraState.BlendHintValue.NoOrientation) == 0)
  883. rot = state.FinalOrientation;
  884. // Avoid dirtying the scene with insignificant rotations diffs
  885. target.ConservativeSetPositionAndRotation(pos, rot);
  886. if ((state.BlendHint & CameraState.BlendHintValue.NoLens) == 0)
  887. {
  888. Camera cam = OutputCamera;
  889. if (cam != null)
  890. {
  891. cam.nearClipPlane = state.Lens.NearClipPlane;
  892. cam.farClipPlane = state.Lens.FarClipPlane;
  893. cam.orthographicSize = state.Lens.OrthographicSize;
  894. cam.fieldOfView = state.Lens.FieldOfView;
  895. cam.lensShift = state.Lens.LensShift;
  896. if (state.Lens.ModeOverride != LensSettings.OverrideModes.None)
  897. cam.orthographic = state.Lens.Orthographic;
  898. bool isPhysical = state.Lens.ModeOverride == LensSettings.OverrideModes.None
  899. ? cam.usePhysicalProperties : state.Lens.IsPhysicalCamera;
  900. cam.usePhysicalProperties = isPhysical;
  901. if (isPhysical && state.Lens.IsPhysicalCamera)
  902. {
  903. cam.sensorSize = state.Lens.SensorSize;
  904. cam.gateFit = state.Lens.GateFit;
  905. cam.focalLength = Camera.FieldOfViewToFocalLength(state.Lens.FieldOfView, state.Lens.SensorSize.y);
  906. #if UNITY_2022_2_OR_NEWER
  907. cam.focusDistance = state.Lens.FocusDistance;
  908. #endif
  909. #if CINEMACHINE_HDRP
  910. #if CINEMACHINE_HDRP_14
  911. cam.iso = state.Lens.Iso;
  912. cam.shutterSpeed = state.Lens.ShutterSpeed;
  913. cam.aperture = state.Lens.Aperture;
  914. cam.bladeCount = state.Lens.BladeCount;
  915. cam.curvature = state.Lens.Curvature;
  916. cam.barrelClipping = state.Lens.BarrelClipping;
  917. cam.anamorphism = state.Lens.Anamorphism;
  918. #else
  919. cam.TryGetComponent<HDAdditionalCameraData>(out var hda);
  920. if (hda != null)
  921. {
  922. hda.physicalParameters.iso = state.Lens.Iso;
  923. hda.physicalParameters.shutterSpeed = state.Lens.ShutterSpeed;
  924. hda.physicalParameters.aperture = state.Lens.Aperture;
  925. hda.physicalParameters.bladeCount = state.Lens.BladeCount;
  926. hda.physicalParameters.curvature = state.Lens.Curvature;
  927. hda.physicalParameters.barrelClipping = state.Lens.BarrelClipping;
  928. hda.physicalParameters.anamorphism = state.Lens.Anamorphism;
  929. }
  930. #endif
  931. #endif
  932. }
  933. }
  934. }
  935. if (CinemachineCore.CameraUpdatedEvent != null)
  936. CinemachineCore.CameraUpdatedEvent.Invoke(this);
  937. }
  938. }
  939. }