CinemachineVirtualCameraBase.cs 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Cinemachine.Utility;
  5. using UnityEngine;
  6. using UnityEngine.Serialization;
  7. namespace Cinemachine
  8. {
  9. /// <summary>
  10. /// Base class for a Monobehaviour that represents a Virtual Camera within the Unity scene.
  11. ///
  12. /// This is intended to be attached to an empty Transform GameObject.
  13. /// Inherited classes can be either standalone virtual cameras such
  14. /// as CinemachineVirtualCamera, or meta-cameras such as
  15. /// CinemachineClearShot or CinemachineFreeLook.
  16. ///
  17. /// A CinemachineVirtualCameraBase exposes a Priority property. When the behaviour is
  18. /// enabled in the game, the Virtual Camera is automatically placed in a queue
  19. /// maintained by the static CinemachineCore singleton.
  20. /// The queue is sorted by priority. When a Unity camera is equipped with a
  21. /// CinemachineBrain behaviour, the brain will choose the camera
  22. /// at the head of the queue. If you have multiple Unity cameras with CinemachineBrain
  23. /// behaviours (say in a split-screen context), then you can filter the queue by
  24. /// setting the culling flags on the virtual cameras. The culling mask of the
  25. /// Unity Camera will then act as a filter for the brain. Apart from this,
  26. /// there is nothing that prevents a virtual camera from controlling multiple
  27. /// Unity cameras simultaneously.
  28. /// </summary>
  29. [SaveDuringPlay]
  30. public abstract class CinemachineVirtualCameraBase : MonoBehaviour, ICinemachineCamera, ISerializationCallbackReceiver
  31. {
  32. /// <summary>Inspector control - Use for hiding sections of the Inspector UI.</summary>
  33. [HideInInspector, SerializeField, NoSaveDuringPlay]
  34. public string[] m_ExcludedPropertiesInInspector = new string[] { "m_Script" };
  35. /// <summary>Inspector control - Use for enabling sections of the Inspector UI.</summary>
  36. [HideInInspector, SerializeField, NoSaveDuringPlay]
  37. public CinemachineCore.Stage[] m_LockStageInInspector;
  38. /// <summary>Version that was last streamed, for upgrading legacy</summary>
  39. public int ValidatingStreamVersion
  40. {
  41. get { return m_OnValidateCalled ? m_ValidatingStreamVersion : CinemachineCore.kStreamingVersion; }
  42. private set { m_ValidatingStreamVersion = value; }
  43. }
  44. private int m_ValidatingStreamVersion = 0;
  45. private bool m_OnValidateCalled = false;
  46. [HideInInspector, SerializeField, NoSaveDuringPlay]
  47. private int m_StreamingVersion;
  48. /// <summary>The priority will determine which camera becomes active based on the
  49. /// state of other cameras and this camera. Higher numbers have greater priority.
  50. /// </summary>
  51. [NoSaveDuringPlay]
  52. [Tooltip("The priority will determine which camera becomes active based on the state of "
  53. + "other cameras and this camera. Higher numbers have greater priority.")]
  54. public int m_Priority = 10;
  55. /// <summary>A sequence number that represents object activation order of vcams.
  56. /// Used for priority sorting.</summary>
  57. internal int m_ActivationId;
  58. /// <summary>
  59. /// This must be set every frame at the start of the pipeline to relax the virtual camera's
  60. /// attachment to the target. Range is 0...1.
  61. /// 1 is full attachment, and is the normal state.
  62. /// 0 is no attachment, and virtual camera will behave as if no Follow
  63. /// targets are set.
  64. /// </summary>
  65. [NonSerialized]
  66. public float FollowTargetAttachment;
  67. /// <summary>
  68. /// This must be set every frame at the start of the pipeline to relax the virtual camera's
  69. /// attachment to the target. Range is 0...1.
  70. /// 1 is full attachment, and is the normal state.
  71. /// 0 is no attachment, and virtual camera will behave as if no LookAt
  72. /// targets are set.
  73. /// </summary>
  74. [NonSerialized]
  75. public float LookAtTargetAttachment;
  76. /// <summary>
  77. /// How often to update a virtual camera when it is in Standby mode
  78. /// </summary>
  79. public enum StandbyUpdateMode
  80. {
  81. /// <summary>Only update if the virtual camera is Live</summary>
  82. Never,
  83. /// <summary>Update the virtual camera every frame, even when it is not Live</summary>
  84. Always,
  85. /// <summary>Update the virtual camera occasionally, the exact frequency depends
  86. /// on how many other virtual cameras are in Standby</summary>
  87. RoundRobin
  88. };
  89. /// <summary>When the virtual camera is not live, this is how often the virtual camera will
  90. /// be updated. Set this to tune for performance. Most of the time Never is fine, unless
  91. /// the virtual camera is doing shot evaluation.
  92. /// </summary>
  93. [Tooltip("When the virtual camera is not live, this is how often the virtual camera will be updated. "
  94. + "Set this to tune for performance. Most of the time Never is fine, "
  95. + "unless the virtual camera is doing shot evaluation.")]
  96. public StandbyUpdateMode m_StandbyUpdate = StandbyUpdateMode.RoundRobin;
  97. /// <summary>
  98. /// Query components and extensions for the maximum damping time.
  99. /// Base class implementation queries extensions.
  100. /// Only used in editor for timeline scrubbing.
  101. /// </summary>
  102. /// <returns>Highest damping setting in this vcam</returns>
  103. public virtual float GetMaxDampTime()
  104. {
  105. float maxDamp = 0;
  106. if (mExtensions != null)
  107. for (int i = 0; i < mExtensions.Count; ++i)
  108. maxDamp = Mathf.Max(maxDamp, mExtensions[i].GetMaxDampTime());
  109. return maxDamp;
  110. }
  111. /// <summary>Get a damped version of a quantity. This is the portion of the
  112. /// quantity that will take effect over the given time.
  113. /// This method takes the target attachment into account. For general
  114. /// damping without consideration of target attachment, use Damper.Damp()</summary>
  115. /// <param name="initial">The amount that will be damped</param>
  116. /// <param name="dampTime">The rate of damping. This is the time it would
  117. /// take to reduce the original amount to a negligible percentage</param>
  118. /// <param name="deltaTime">The time over which to damp</param>
  119. /// <returns>The damped amount. This will be the original amount scaled by
  120. /// a value between 0 and 1.</returns>
  121. public float DetachedFollowTargetDamp(float initial, float dampTime, float deltaTime)
  122. {
  123. dampTime = Mathf.Lerp(Mathf.Max(1, dampTime), dampTime, FollowTargetAttachment);
  124. deltaTime = Mathf.Lerp(0, deltaTime, FollowTargetAttachment);
  125. return Damper.Damp(initial, dampTime, deltaTime);
  126. }
  127. /// <summary>Get a damped version of a quantity. This is the portion of the
  128. /// quantity that will take effect over the given time.
  129. /// This method takes the target attachment into account. For general
  130. /// damping without consideration of target attachment, use Damper.Damp()</summary>
  131. /// <param name="initial">The amount that will be damped</param>
  132. /// <param name="dampTime">The rate of damping. This is the time it would
  133. /// take to reduce the original amount to a negligible percentage</param>
  134. /// <param name="deltaTime">The time over which to damp</param>
  135. /// <returns>The damped amount. This will be the original amount scaled by
  136. /// a value between 0 and 1.</returns>
  137. public Vector3 DetachedFollowTargetDamp(Vector3 initial, Vector3 dampTime, float deltaTime)
  138. {
  139. dampTime = Vector3.Lerp(Vector3.Max(Vector3.one, dampTime), dampTime, FollowTargetAttachment);
  140. deltaTime = Mathf.Lerp(0, deltaTime, FollowTargetAttachment);
  141. return Damper.Damp(initial, dampTime, deltaTime);
  142. }
  143. /// <summary>Get a damped version of a quantity. This is the portion of the
  144. /// quantity that will take effect over the given time.
  145. /// This method takes the target attachment into account. For general
  146. /// damping without consideration of target attachment, use Damper.Damp()</summary>
  147. /// <param name="initial">The amount that will be damped</param>
  148. /// <param name="dampTime">The rate of damping. This is the time it would
  149. /// take to reduce the original amount to a negligible percentage</param>
  150. /// <param name="deltaTime">The time over which to damp</param>
  151. /// <returns>The damped amount. This will be the original amount scaled by
  152. /// a value between 0 and 1.</returns>
  153. public Vector3 DetachedFollowTargetDamp(Vector3 initial, float dampTime, float deltaTime)
  154. {
  155. dampTime = Mathf.Lerp(Mathf.Max(1, dampTime), dampTime, FollowTargetAttachment);
  156. deltaTime = Mathf.Lerp(0, deltaTime, FollowTargetAttachment);
  157. return Damper.Damp(initial, dampTime, deltaTime);
  158. }
  159. /// <summary>Get a damped version of a quantity. This is the portion of the
  160. /// quantity that will take effect over the given time.
  161. /// This method takes the target attachment into account. For general
  162. /// damping without consideration of target attachment, use Damper.Damp()</summary>
  163. /// <param name="initial">The amount that will be damped</param>
  164. /// <param name="dampTime">The rate of damping. This is the time it would
  165. /// take to reduce the original amount to a negligible percentage</param>
  166. /// <param name="deltaTime">The time over which to damp</param>
  167. /// <returns>The damped amount. This will be the original amount scaled by
  168. /// a value between 0 and 1.</returns>
  169. public float DetachedLookAtTargetDamp(float initial, float dampTime, float deltaTime)
  170. {
  171. dampTime = Mathf.Lerp(Mathf.Max(1, dampTime), dampTime, LookAtTargetAttachment);
  172. deltaTime = Mathf.Lerp(0, deltaTime, LookAtTargetAttachment);
  173. return Damper.Damp(initial, dampTime, deltaTime);
  174. }
  175. /// <summary>Get a damped version of a quantity. This is the portion of the
  176. /// quantity that will take effect over the given time.
  177. /// This method takes the target attachment into account. For general
  178. /// damping without consideration of target attachment, use Damper.Damp()</summary>
  179. /// <param name="initial">The amount that will be damped</param>
  180. /// <param name="dampTime">The rate of damping. This is the time it would
  181. /// take to reduce the original amount to a negligible percentage</param>
  182. /// <param name="deltaTime">The time over which to damp</param>
  183. /// <returns>The damped amount. This will be the original amount scaled by
  184. /// a value between 0 and 1.</returns>
  185. public Vector3 DetachedLookAtTargetDamp(Vector3 initial, Vector3 dampTime, float deltaTime)
  186. {
  187. dampTime = Vector3.Lerp(Vector3.Max(Vector3.one, dampTime), dampTime, LookAtTargetAttachment);
  188. deltaTime = Mathf.Lerp(0, deltaTime, LookAtTargetAttachment);
  189. return Damper.Damp(initial, dampTime, deltaTime);
  190. }
  191. /// <summary>Get a damped version of a quantity. This is the portion of the
  192. /// quantity that will take effect over the given time.
  193. /// This method takes the target attachment into account. For general
  194. /// damping without consideration of target attachment, use Damper.Damp()</summary>
  195. /// <param name="initial">The amount that will be damped</param>
  196. /// <param name="dampTime">The rate of damping. This is the time it would
  197. /// take to reduce the original amount to a negligible percentage</param>
  198. /// <param name="deltaTime">The time over which to damp</param>
  199. /// <returns>The damped amount. This will be the original amount scaled by
  200. /// a value between 0 and 1.</returns>
  201. public Vector3 DetachedLookAtTargetDamp(Vector3 initial, float dampTime, float deltaTime)
  202. {
  203. dampTime = Mathf.Lerp(Mathf.Max(1, dampTime), dampTime, LookAtTargetAttachment);
  204. deltaTime = Mathf.Lerp(0, deltaTime, LookAtTargetAttachment);
  205. return Damper.Damp(initial, dampTime, deltaTime);
  206. }
  207. /// <summary>
  208. /// A delegate to hook into the state calculation pipeline.
  209. /// This will be called after each pipeline stage, to allow others to hook into the pipeline.
  210. /// See CinemachineCore.Stage.
  211. /// </summary>
  212. /// <param name="extension">The extension to add.</param>
  213. public virtual void AddExtension(CinemachineExtension extension)
  214. {
  215. if (mExtensions == null)
  216. mExtensions = new List<CinemachineExtension>();
  217. else
  218. mExtensions.Remove(extension);
  219. mExtensions.Add(extension);
  220. }
  221. /// <summary>Remove a Pipeline stage hook callback.</summary>
  222. /// <param name="extension">The extension to remove.</param>
  223. public virtual void RemoveExtension(CinemachineExtension extension)
  224. {
  225. if (mExtensions != null)
  226. mExtensions.Remove(extension);
  227. }
  228. /// <summary> The extensions connected to this vcam</summary>
  229. internal List<CinemachineExtension> mExtensions { get; private set; }
  230. /// <summary>
  231. /// Invokes the PostPipelineStageDelegate for this camera, and up the hierarchy for all
  232. /// parent cameras (if any).
  233. /// Implementaion must be sure to call this after each pipeline stage, to allow
  234. /// other services to hook into the pipeline.
  235. /// See CinemachineCore.Stage.
  236. /// </summary>
  237. /// <param name="vcam">The virtual camera being processed</param>
  238. /// <param name="stage">The current pipeline stage</param>
  239. /// <param name="newState">The current virtual camera state</param>
  240. /// <param name="deltaTime">The current applicable deltaTime</param>
  241. protected void InvokePostPipelineStageCallback(
  242. CinemachineVirtualCameraBase vcam, CinemachineCore.Stage stage,
  243. ref CameraState newState, float deltaTime)
  244. {
  245. if (mExtensions != null)
  246. {
  247. for (int i = 0; i < mExtensions.Count; ++i)
  248. {
  249. var e = mExtensions[i];
  250. if (e == null)
  251. {
  252. // Object was deleted (possibly because of Undo in the editor)
  253. mExtensions.RemoveAt(i);
  254. --i;
  255. }
  256. else if (e.enabled)
  257. e.InvokePostPipelineStageCallback(vcam, stage, ref newState, deltaTime);
  258. }
  259. }
  260. CinemachineVirtualCameraBase parent = ParentCamera as CinemachineVirtualCameraBase;
  261. if (parent != null)
  262. parent.InvokePostPipelineStageCallback(vcam, stage, ref newState, deltaTime);
  263. }
  264. /// <summary>
  265. /// Invokes the PrePipelineMutateCameraStateCallback for this camera,
  266. /// and up the hierarchy for all parent cameras (if any).
  267. /// Implementaion must be sure to call this after each pipeline stage, to allow
  268. /// other services to hook into the pipeline.
  269. /// See CinemachineCore.Stage.
  270. /// </summary>
  271. /// <param name="vcam">The virtual camera being processed</param>
  272. /// <param name="newState">The current virtual camera state</param>
  273. /// <param name="deltaTime">The current applicable deltaTime</param>
  274. protected void InvokePrePipelineMutateCameraStateCallback(
  275. CinemachineVirtualCameraBase vcam, ref CameraState newState, float deltaTime)
  276. {
  277. if (mExtensions != null)
  278. {
  279. for (int i = 0; i < mExtensions.Count; ++i)
  280. {
  281. var e = mExtensions[i];
  282. if (e == null)
  283. {
  284. // Object was deleted (possibly because of Undo in the editor)
  285. mExtensions.RemoveAt(i);
  286. --i;
  287. }
  288. else if (e.enabled)
  289. e.PrePipelineMutateCameraStateCallback(vcam, ref newState, deltaTime);
  290. }
  291. }
  292. CinemachineVirtualCameraBase parent = ParentCamera as CinemachineVirtualCameraBase;
  293. if (parent != null)
  294. parent.InvokePrePipelineMutateCameraStateCallback(vcam, ref newState, deltaTime);
  295. }
  296. /// <summary>
  297. /// Invokes the OnTransitionFromCamera for all extensions on this camera
  298. /// </summary>
  299. /// <param name="fromCam">The camera being deactivated. May be null.</param>
  300. /// <param name="worldUp">Default world Up, set by the CinemachineBrain</param>
  301. /// <param name="deltaTime">Delta time for time-based effects (ignore if less than or equal to 0)</param>
  302. /// <returns>True to request a vcam update of internal state</returns>
  303. protected bool InvokeOnTransitionInExtensions(
  304. ICinemachineCamera fromCam, Vector3 worldUp, float deltaTime)
  305. {
  306. bool forceUpdate = false;
  307. if (mExtensions != null)
  308. {
  309. for (int i = 0; i < mExtensions.Count; ++i)
  310. {
  311. var e = mExtensions[i];
  312. if (e == null)
  313. {
  314. // Object was deleted (possibly because of Undo in the editor)
  315. mExtensions.RemoveAt(i);
  316. --i;
  317. }
  318. else if (e.enabled && e.OnTransitionFromCamera(fromCam, worldUp, deltaTime))
  319. forceUpdate = true;
  320. }
  321. }
  322. return forceUpdate;
  323. }
  324. /// <summary>Get the name of the Virtual Camera. Base implementation
  325. /// returns the owner GameObject's name.</summary>
  326. public string Name => name;
  327. /// <summary>Gets a brief debug description of this virtual camera, for use when displayiong debug info</summary>
  328. public virtual string Description => "";
  329. /// <summary>Get the Priority of the virtual camera. This determines its placement
  330. /// in the CinemachineCore's queue of eligible shots.</summary>
  331. public int Priority {
  332. get => m_Priority;
  333. set => m_Priority = value;
  334. }
  335. /// <summary>Hint for blending to and from this virtual camera</summary>
  336. public enum BlendHint
  337. {
  338. /// <summary>Standard linear position and aim blend</summary>
  339. None,
  340. /// <summary>Spherical blend about LookAt target position if there is a LookAt target, linear blend between LookAt targets</summary>
  341. SphericalPosition,
  342. /// <summary>Cylindrical blend about LookAt target position if there is a LookAt target (vertical co-ordinate is linearly interpolated), linear blend between LookAt targets</summary>
  343. CylindricalPosition,
  344. /// <summary>Standard linear position blend, radial blend between LookAt targets</summary>
  345. ScreenSpaceAimWhenTargetsDiffer
  346. }
  347. /// <summary>Applies a position blend hint to a camera state</summary>
  348. /// <param name="state">The state to apply the hint to</param>
  349. /// <param name="hint">The hint to apply</param>
  350. protected void ApplyPositionBlendMethod(ref CameraState state, BlendHint hint)
  351. {
  352. switch (hint)
  353. {
  354. default:
  355. break;
  356. case BlendHint.SphericalPosition:
  357. state.BlendHint |= CameraState.BlendHintValue.SphericalPositionBlend;
  358. break;
  359. case BlendHint.CylindricalPosition:
  360. state.BlendHint |= CameraState.BlendHintValue.CylindricalPositionBlend;
  361. break;
  362. case BlendHint.ScreenSpaceAimWhenTargetsDiffer:
  363. state.BlendHint |= CameraState.BlendHintValue.RadialAimBlend;
  364. break;
  365. }
  366. }
  367. /// <summary>The GameObject owner of the Virtual Camera behaviour.</summary>
  368. public GameObject VirtualCameraGameObject
  369. {
  370. get
  371. {
  372. if (this == null)
  373. return null; // object deleted
  374. return gameObject;
  375. }
  376. }
  377. /// <summary>Returns false if the object has been deleted</summary>
  378. public bool IsValid => !(this == null);
  379. /// <summary>The CameraState object holds all of the information
  380. /// necessary to position the Unity camera. It is the output of this class.</summary>
  381. public abstract CameraState State { get; }
  382. /// <summary>Support for meta-virtual-cameras. This is the situation where a
  383. /// virtual camera is in fact the public face of a private army of virtual cameras, which
  384. /// it manages on its own. This method gets the VirtualCamera owner, if any.
  385. /// Private armies are implemented as Transform children of the parent vcam.</summary>
  386. public ICinemachineCamera ParentCamera
  387. {
  388. get
  389. {
  390. if (!mSlaveStatusUpdated || !Application.isPlaying)
  391. UpdateSlaveStatus();
  392. return m_parentVcam;
  393. }
  394. }
  395. /// <summary>Check whether the vcam a live child of this camera.
  396. /// This base class implementation always returns false.</summary>
  397. /// <param name="vcam">The Virtual Camera to check</param>
  398. /// <param name="dominantChildOnly">If truw, will only return true if this vcam is the dominat live child</param>
  399. /// <returns>True if the vcam is currently actively influencing the state of this vcam</returns>
  400. public virtual bool IsLiveChild(ICinemachineCamera vcam, bool dominantChildOnly = false) { return false; }
  401. /// <summary>Get the LookAt target for the Aim component in the Cinemachine pipeline.</summary>
  402. public abstract Transform LookAt { get; set; }
  403. /// <summary>Get the Follow target for the Body component in the Cinemachine pipeline.</summary>
  404. public abstract Transform Follow { get; set; }
  405. /// <summary>Set this to force the next update to ignore state from the previous frame.
  406. /// This is useful, for example, if you want to cancel damping or other time-based processing.</summary>
  407. public virtual bool PreviousStateIsValid { get; set; }
  408. /// <summary>
  409. /// Update the camera's state.
  410. /// The implementation must guarantee against multiple calls per frame, and should
  411. /// use CinemachineCore.UpdateVirtualCamera(ICinemachineCamera, Vector3, float), which
  412. /// has protection against multiple calls per frame.
  413. /// </summary>
  414. /// <param name="worldUp">Default world Up, set by the CinemachineBrain</param>
  415. /// <param name="deltaTime">Delta time for time-based effects (ignore if less than 0)</param>
  416. public void UpdateCameraState(Vector3 worldUp, float deltaTime)
  417. {
  418. CinemachineCore.Instance.UpdateVirtualCamera(this, worldUp, deltaTime);
  419. }
  420. /// <summary>Internal use only. Do not call this method.
  421. /// Called by CinemachineCore at designated update time
  422. /// so the vcam can position itself and track its targets.
  423. /// Do not call this method. Let the framework do it at the appropriate time</summary>
  424. /// <param name="worldUp">Default world Up, set by the CinemachineBrain</param>
  425. /// <param name="deltaTime">Delta time for time-based effects (ignore if less than 0)</param>
  426. public abstract void InternalUpdateCameraState(Vector3 worldUp, float deltaTime);
  427. /// <summary> Collection of parameters that influence how this virtual camera transitions from
  428. /// other virtual cameras </summary>
  429. [Serializable]
  430. public struct TransitionParams
  431. {
  432. /// <summary>Hint for blending positions to and from this virtual camera</summary>
  433. [Tooltip("Hint for blending positions to and from this virtual camera")]
  434. [FormerlySerializedAs("m_PositionBlending")]
  435. public BlendHint m_BlendHint;
  436. /// <summary>When this virtual camera goes Live, attempt to force the position to be the same as the current position of the Unity Camera</summary>
  437. [Tooltip("When this virtual camera goes Live, attempt to force the position to be the same as the current position of the Unity Camera")]
  438. public bool m_InheritPosition;
  439. /// <summary>This event fires when the virtual camera goes Live</summary>
  440. [Tooltip("This event fires when the virtual camera goes Live")]
  441. public CinemachineBrain.VcamActivatedEvent m_OnCameraLive;
  442. }
  443. /// <summary>Notification that this virtual camera is going live.
  444. /// Base class implementation must be called by any overridden method.</summary>
  445. /// <param name="fromCam">The camera being deactivated. May be null.</param>
  446. /// <param name="worldUp">Default world Up, set by the CinemachineBrain</param>
  447. /// <param name="deltaTime">Delta time for time-based effects (ignore if less than or equal to 0)</param>
  448. public virtual void OnTransitionFromCamera(
  449. ICinemachineCamera fromCam, Vector3 worldUp, float deltaTime)
  450. {
  451. if (!gameObject.activeInHierarchy)
  452. PreviousStateIsValid = false;
  453. }
  454. /// <summary>Maintains the global vcam registry. Always call the base class implementation.</summary>
  455. protected virtual void OnDestroy()
  456. {
  457. CinemachineCore.Instance.CameraDestroyed(this);
  458. }
  459. /// <summary>Base class implementation makes sure the priority queue remains up-to-date.</summary>
  460. protected virtual void OnTransformParentChanged()
  461. {
  462. CinemachineCore.Instance.CameraDisabled(this);
  463. CinemachineCore.Instance.CameraEnabled(this);
  464. UpdateSlaveStatus();
  465. UpdateVcamPoolStatus();
  466. }
  467. bool m_WasStarted;
  468. /// <summary>Derived classes should call base class implementation.</summary>
  469. protected virtual void Start()
  470. {
  471. m_WasStarted = true;
  472. }
  473. /// <summary>
  474. /// Returns true, when the vcam has an extension that requires user input.
  475. /// </summary>
  476. internal virtual bool RequiresUserInput()
  477. {
  478. return mExtensions != null && mExtensions.Any(extension => extension != null && extension.RequiresUserInput);
  479. }
  480. /// <summary>
  481. /// Called on inactive object when being artificially activated by timeline.
  482. /// This is necessary because Awake() isn't called on inactive gameObjects.
  483. /// </summary>
  484. internal void EnsureStarted()
  485. {
  486. if (!m_WasStarted)
  487. {
  488. m_WasStarted = true;
  489. var extensions = GetComponentsInChildren<CinemachineExtension>();
  490. for (int i = 0; i < extensions.Length; ++i)
  491. extensions[i].EnsureStarted();
  492. }
  493. }
  494. #if UNITY_EDITOR
  495. [UnityEditor.Callbacks.DidReloadScripts]
  496. static void OnScriptReload()
  497. {
  498. var vcams = Resources.FindObjectsOfTypeAll(
  499. typeof(CinemachineVirtualCameraBase)) as CinemachineVirtualCameraBase[];
  500. foreach (var vcam in vcams)
  501. vcam.LookAtTargetChanged = vcam.FollowTargetChanged = true;
  502. }
  503. #endif
  504. /// <summary>
  505. /// Locate the first component that implements AxisState.IInputAxisProvider.
  506. /// </summary>
  507. /// <returns>The first AxisState.IInputAxisProvider or null if none</returns>
  508. public AxisState.IInputAxisProvider GetInputAxisProvider()
  509. {
  510. var components = GetComponentsInChildren<MonoBehaviour>();
  511. for (int i = 0; i < components.Length; ++i)
  512. {
  513. var provider = components[i] as AxisState.IInputAxisProvider;
  514. if (provider != null)
  515. return provider;
  516. }
  517. return null;
  518. }
  519. /// <summary>Enforce bounds for fields, when changed in inspector.
  520. /// Call base class implementation at the beginning of overridden method.
  521. /// After base method is called, ValidatingStreamVersion will be valid.</summary>
  522. protected virtual void OnValidate()
  523. {
  524. m_OnValidateCalled = true;
  525. ValidatingStreamVersion = m_StreamingVersion;
  526. m_StreamingVersion = CinemachineCore.kStreamingVersion;
  527. }
  528. /// <summary>Base class implementation adds the virtual camera from the priority queue.</summary>
  529. protected virtual void OnEnable()
  530. {
  531. UpdateSlaveStatus();
  532. UpdateVcamPoolStatus(); // Add to queue
  533. if (!CinemachineCore.Instance.IsLive(this))
  534. PreviousStateIsValid = false;
  535. CinemachineCore.Instance.CameraEnabled(this);
  536. InvalidateCachedTargets();
  537. // Sanity check - if another vcam component is enabled, shut down
  538. var vcamComponents = GetComponents<CinemachineVirtualCameraBase>();
  539. for (int i = 0; i < vcamComponents.Length; ++i)
  540. {
  541. if (vcamComponents[i].enabled && vcamComponents[i] != this)
  542. {
  543. Debug.LogError(Name
  544. + " has multiple CinemachineVirtualCameraBase-derived components. Disabling "
  545. + GetType().Name + ".");
  546. enabled = false;
  547. }
  548. }
  549. }
  550. /// <summary>Base class implementation makes sure the priority queue remains up-to-date.</summary>
  551. protected virtual void OnDisable()
  552. {
  553. UpdateVcamPoolStatus(); // Remove from queue
  554. CinemachineCore.Instance.CameraDisabled(this);
  555. }
  556. /// <summary>Base class implementation makes sure the priority queue remains up-to-date.</summary>
  557. protected virtual void Update()
  558. {
  559. if (m_Priority != m_QueuePriority)
  560. {
  561. UpdateVcamPoolStatus(); // Force a re-sort
  562. }
  563. }
  564. private bool mSlaveStatusUpdated = false;
  565. private CinemachineVirtualCameraBase m_parentVcam = null;
  566. private void UpdateSlaveStatus()
  567. {
  568. mSlaveStatusUpdated = true;
  569. m_parentVcam = null;
  570. Transform p = transform.parent;
  571. if (p != null)
  572. {
  573. #if UNITY_2019_2_OR_NEWER
  574. p.TryGetComponent(out m_parentVcam);
  575. #else
  576. m_parentVcam = p.GetComponent<CinemachineVirtualCameraBase>();
  577. #endif
  578. }
  579. }
  580. /// <summary>Returns this vcam's LookAt target, or if that is null, will retrun
  581. /// the parent vcam's LookAt target.</summary>
  582. /// <param name="localLookAt">This vcam's LookAt value.</param>
  583. /// <returns>The same value, or the parent's if null and a parent exists.</returns>
  584. public Transform ResolveLookAt(Transform localLookAt)
  585. {
  586. Transform lookAt = localLookAt;
  587. if (lookAt == null && ParentCamera != null)
  588. lookAt = ParentCamera.LookAt; // Parent provides default
  589. return lookAt;
  590. }
  591. /// <summary>Returns this vcam's Follow target, or if that is null, will retrun
  592. /// the parent vcam's Follow target.</summary>
  593. /// <param name="localFollow">This vcam's Follow value.</param>
  594. /// <returns>The same value, or the parent's if null and a parent exists.</returns>
  595. public Transform ResolveFollow(Transform localFollow)
  596. {
  597. Transform follow = localFollow;
  598. if (follow == null && ParentCamera != null)
  599. follow = ParentCamera.Follow; // Parent provides default
  600. return follow;
  601. }
  602. private int m_QueuePriority = int.MaxValue;
  603. private void UpdateVcamPoolStatus()
  604. {
  605. CinemachineCore.Instance.RemoveActiveCamera(this);
  606. if (m_parentVcam == null && isActiveAndEnabled)
  607. CinemachineCore.Instance.AddActiveCamera(this);
  608. m_QueuePriority = m_Priority;
  609. }
  610. /// <summary>When multiple virtual cameras have the highest priority, there is
  611. /// sometimes the need to push one to the top, making it the current Live camera if
  612. /// it shares the highest priority in the queue with its peers.
  613. ///
  614. /// This happens automatically when a
  615. /// new vcam is enabled: the most recent one goes to the top of the priority subqueue.
  616. /// Use this method to push a vcam to the top of its priority peers.
  617. /// If it and its peers share the highest priority, then this vcam will become Live.</summary>
  618. public void MoveToTopOfPrioritySubqueue()
  619. {
  620. UpdateVcamPoolStatus(); // Force a re-sort
  621. }
  622. /// <summary>This is called to notify the component that a target got warped,
  623. /// so that the component can update its internal state to make the camera
  624. /// also warp seamlessy.</summary>
  625. /// <param name="target">The object that was warped</param>
  626. /// <param name="positionDelta">The amount the target's position changed</param>
  627. public virtual void OnTargetObjectWarped(Transform target, Vector3 positionDelta)
  628. {
  629. // inform the extensions
  630. if (mExtensions != null)
  631. {
  632. for (int i = 0; i < mExtensions.Count; ++i)
  633. mExtensions[i].OnTargetObjectWarped(target, positionDelta);
  634. }
  635. }
  636. /// <summary>
  637. /// Force the virtual camera to assume a given position and orientation
  638. /// </summary>
  639. /// <param name="pos">Worldspace pposition to take</param>
  640. /// <param name="rot">Worldspace orientation to take</param>
  641. public virtual void ForceCameraPosition(Vector3 pos, Quaternion rot)
  642. {
  643. // inform the extensions
  644. if (mExtensions != null)
  645. {
  646. for (int i = 0; i < mExtensions.Count; ++i)
  647. mExtensions[i].ForceCameraPosition(pos, rot);
  648. }
  649. }
  650. // Doesn't really belong here but putting it here to avoid changing
  651. // the API (belongs in the caller of CreateBlend). GML todo: Fix in next version.
  652. float m_blendStartPosition;
  653. // This is a total hack, because of missing API call. GML todo: Fix in next version.
  654. bool GetInheritPosition(ICinemachineCamera cam)
  655. {
  656. if (cam is CinemachineVirtualCamera)
  657. return (cam as CinemachineVirtualCamera).m_Transitions.m_InheritPosition;
  658. if (cam is CinemachineFreeLook)
  659. return (cam as CinemachineFreeLook).m_Transitions.m_InheritPosition;
  660. return false;
  661. }
  662. /// <summary>Create a blend between 2 virtual cameras, taking into account
  663. /// any existing active blend, with special case handling if the new blend is
  664. /// effectively an undo of the current blend</summary>
  665. /// <param name="camA">Outgoing virtual camera</param>
  666. /// <param name="camB">Incoming virtual camera</param>
  667. /// <param name="blendDef">Definition of the blend to create</param>
  668. /// <param name="activeBlend">The current active blend</param>
  669. /// <returns>The new blend</returns>
  670. protected CinemachineBlend CreateBlend(
  671. ICinemachineCamera camA, ICinemachineCamera camB,
  672. CinemachineBlendDefinition blendDef,
  673. CinemachineBlend activeBlend)
  674. {
  675. if (blendDef.BlendCurve == null || blendDef.BlendTime <= 0 || (camA == null && camB == null))
  676. {
  677. m_blendStartPosition = 0;
  678. return null;
  679. }
  680. if (activeBlend != null)
  681. {
  682. // Special case: if backing out of a blend-in-progress
  683. // with the same blend in reverse, adjust the blend time
  684. // to cancel out the progress made in the opposite direction
  685. if (activeBlend != null && !activeBlend.IsComplete && activeBlend.CamA == camB && activeBlend.CamB == camA)
  686. {
  687. // How far have we blended? That is what we must undo
  688. var progress = m_blendStartPosition
  689. + (1 - m_blendStartPosition) * activeBlend.TimeInBlend / activeBlend.Duration;
  690. blendDef.m_Time *= progress;
  691. m_blendStartPosition = 1 - progress;
  692. }
  693. else
  694. m_blendStartPosition = 0;
  695. if (GetInheritPosition(camB))
  696. camA = null; // otherwise we get a pop when camB is moved
  697. else
  698. camA = new BlendSourceVirtualCamera(activeBlend);
  699. }
  700. if (camA == null)
  701. camA = new StaticPointVirtualCamera(State, "(none)");
  702. return new CinemachineBlend(
  703. camA, camB, blendDef.BlendCurve, blendDef.BlendTime, 0);
  704. }
  705. /// <summary>
  706. /// Create a camera state based on the current transform of this vcam
  707. /// </summary>
  708. /// <param name="worldUp">Current World Up direction, as provided by the brain</param>
  709. /// <param name="lens">Lens settings to serve as base, will be combined with lens from brain, if any</param>
  710. /// <returns></returns>
  711. protected CameraState PullStateFromVirtualCamera(Vector3 worldUp, ref LensSettings lens)
  712. {
  713. CameraState state = CameraState.Default;
  714. state.RawPosition = TargetPositionCache.GetTargetPosition(transform);
  715. state.RawOrientation = TargetPositionCache.GetTargetRotation(transform);
  716. state.ReferenceUp = worldUp;
  717. CinemachineBrain brain = CinemachineCore.Instance.FindPotentialTargetBrain(this);
  718. if (brain != null)
  719. lens.SnapshotCameraReadOnlyProperties(brain.OutputCamera);
  720. state.Lens = lens;
  721. return state;
  722. }
  723. private Transform m_CachedFollowTarget;
  724. private CinemachineVirtualCameraBase m_CachedFollowTargetVcam;
  725. private ICinemachineTargetGroup m_CachedFollowTargetGroup;
  726. private Transform m_CachedLookAtTarget;
  727. private CinemachineVirtualCameraBase m_CachedLookAtTargetVcam;
  728. private ICinemachineTargetGroup m_CachedLookAtTargetGroup;
  729. private void InvalidateCachedTargets()
  730. {
  731. m_CachedFollowTarget = null;
  732. m_CachedFollowTargetVcam = null;
  733. m_CachedFollowTargetGroup = null;
  734. m_CachedLookAtTarget = null;
  735. m_CachedLookAtTargetVcam = null;
  736. m_CachedLookAtTargetGroup = null;
  737. }
  738. #if UNITY_EDITOR
  739. [UnityEditor.InitializeOnLoad]
  740. class OnDomainReload
  741. {
  742. static OnDomainReload()
  743. {
  744. #if UNITY_2023_1_OR_NEWER
  745. var vcams = FindObjectsByType<CinemachineVirtualCameraBase>
  746. (FindObjectsInactive.Include, FindObjectsSortMode.None);
  747. #elif UNITY_2020_1_OR_NEWER
  748. var vcams = FindObjectsOfType<CinemachineVirtualCameraBase>(true);
  749. #else
  750. var vcams = FindObjectsOfType<CinemachineVirtualCameraBase>();
  751. #endif
  752. foreach (var vcam in vcams)
  753. vcam.InvalidateCachedTargets();
  754. }
  755. }
  756. #endif
  757. /// <summary>
  758. /// This property is true if the Follow target was changed this frame.
  759. /// </summary>
  760. public bool FollowTargetChanged { get; private set; }
  761. /// <summary>
  762. /// This property is true if the LookAttarget was changed this frame.
  763. /// </summary>
  764. public bool LookAtTargetChanged { get; private set; }
  765. /// <summary>
  766. /// Call this from InternalUpdateCameraState() to check for changed
  767. /// targets and update the target cache. This is needed for tracking
  768. /// when a target object changes.
  769. /// </summary>
  770. protected void UpdateTargetCache()
  771. {
  772. var target = ResolveFollow(Follow);
  773. FollowTargetChanged = target != m_CachedFollowTarget;
  774. if (FollowTargetChanged)
  775. {
  776. m_CachedFollowTarget = target;
  777. m_CachedFollowTargetVcam = null;
  778. m_CachedFollowTargetGroup = null;
  779. if (m_CachedFollowTarget != null)
  780. {
  781. target.TryGetComponent<CinemachineVirtualCameraBase>(out m_CachedFollowTargetVcam);
  782. target.TryGetComponent<ICinemachineTargetGroup>(out m_CachedFollowTargetGroup);
  783. }
  784. }
  785. target = ResolveLookAt(LookAt);
  786. LookAtTargetChanged = target != m_CachedLookAtTarget;
  787. if (LookAtTargetChanged)
  788. {
  789. m_CachedLookAtTarget = target;
  790. m_CachedLookAtTargetVcam = null;
  791. m_CachedLookAtTargetGroup = null;
  792. if (target != null)
  793. {
  794. target.TryGetComponent<CinemachineVirtualCameraBase>(out m_CachedLookAtTargetVcam);
  795. target.TryGetComponent<ICinemachineTargetGroup>(out m_CachedLookAtTargetGroup);
  796. }
  797. }
  798. }
  799. /// <summary>Get Follow target as ICinemachineTargetGroup,
  800. /// or null if target is not a ICinemachineTargetGroup</summary>
  801. public ICinemachineTargetGroup AbstractFollowTargetGroup => m_CachedFollowTargetGroup;
  802. /// <summary>Get Follow target as CinemachineVirtualCameraBase,
  803. /// or null if target is not a CinemachineVirtualCameraBase</summary>
  804. public CinemachineVirtualCameraBase FollowTargetAsVcam => m_CachedFollowTargetVcam;
  805. /// <summary>Get LookAt target as ICinemachineTargetGroup,
  806. /// or null if target is not a ICinemachineTargetGroup</summary>
  807. public ICinemachineTargetGroup AbstractLookAtTargetGroup => m_CachedLookAtTargetGroup;
  808. /// <summary>Get LookAt target as CinemachineVirtualCameraBase,
  809. /// or null if target is not a CinemachineVirtualCameraBase</summary>
  810. public CinemachineVirtualCameraBase LookAtTargetAsVcam => m_CachedLookAtTargetVcam;
  811. /// <summary>Pre-Serialization handler - delegates to derived classes</summary>
  812. void ISerializationCallbackReceiver.OnBeforeSerialize() => OnBeforeSerialize();
  813. /// <summary>Post-Serialization handler - performs legacy upgrade</summary>
  814. void ISerializationCallbackReceiver.OnAfterDeserialize()
  815. {
  816. if (m_StreamingVersion < CinemachineCore.kStreamingVersion)
  817. LegacyUpgrade(m_StreamingVersion);
  818. m_StreamingVersion = CinemachineCore.kStreamingVersion;
  819. }
  820. /// <summary>
  821. /// Override this to handle any upgrades necessitated by a streaming version change
  822. /// </summary>
  823. /// <param name="streamedVersion">The version that was streamed</param>
  824. internal protected virtual void LegacyUpgrade(int streamedVersion) {}
  825. internal virtual void OnBeforeSerialize() {}
  826. /// <summary>
  827. /// Temporarily cancel damping for this frame. The camera will sanp to its target
  828. /// position when it is updated.
  829. /// </summary>
  830. /// <param name="updateNow">If true, snap the camera to its target immediately, otherwise wait
  831. /// until the end of the frame when cameras are normally updated.</param>
  832. public void CancelDamping(bool updateNow = false)
  833. {
  834. PreviousStateIsValid = false;
  835. if (updateNow)
  836. {
  837. var up = State.ReferenceUp;
  838. var brain = CinemachineCore.Instance.FindPotentialTargetBrain(this);
  839. if (brain != null)
  840. up = brain.DefaultWorldUp;
  841. InternalUpdateCameraState(up, -1);
  842. }
  843. }
  844. }
  845. }