CinemachineCore.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. using UnityEngine;
  2. using System.Collections.Generic;
  3. namespace Cinemachine
  4. {
  5. internal static class Documentation
  6. {
  7. /// <summary>This must be used like
  8. /// [HelpURL(Documentation.BaseURL + "api/some-page.html")]
  9. /// or
  10. /// [HelpURL(Documentation.BaseURL + "manual/some-page.html")]
  11. /// It cannot support String.Format nor string interpolation</summary>
  12. public const string BaseURL = "https://docs.unity3d.com/Packages/com.unity.cinemachine@2.6/";
  13. }
  14. /// <summary>A singleton that manages complete lists of CinemachineBrain and,
  15. /// Cinemachine Virtual Cameras, and the priority queue. Provides
  16. /// services to keeping track of whether Cinemachine Virtual Cameras have
  17. /// been updated each frame.</summary>
  18. public sealed class CinemachineCore
  19. {
  20. /// <summary>Data version string. Used to upgrade from legacy projects</summary>
  21. public static readonly int kStreamingVersion = 20170927;
  22. /// <summary>Human-readable Cinemachine Version</summary>
  23. public static readonly string kVersionString = "2.6.11";
  24. /// <summary>
  25. /// Stages in the Cinemachine Component pipeline, used for
  26. /// UI organization>. This enum defines the pipeline order.
  27. /// </summary>
  28. public enum Stage
  29. {
  30. /// <summary>Second stage: position the camera in space</summary>
  31. Body,
  32. /// <summary>Third stage: orient the camera to point at the target</summary>
  33. Aim,
  34. /// <summary>Final pipeline stage: apply noise (this is done separately, in the
  35. /// Correction channel of the CameraState)</summary>
  36. Noise,
  37. /// <summary>Post-correction stage. This is invoked on all virtual camera
  38. /// types, after the pipeline is complete</summary>
  39. Finalize
  40. };
  41. private static CinemachineCore sInstance = null;
  42. /// <summary>Get the singleton instance</summary>
  43. public static CinemachineCore Instance
  44. {
  45. get
  46. {
  47. if (sInstance == null)
  48. sInstance = new CinemachineCore();
  49. return sInstance;
  50. }
  51. }
  52. /// <summary>
  53. /// If true, show hidden Cinemachine objects, to make manual script mapping possible.
  54. /// </summary>
  55. public static bool sShowHiddenObjects = false;
  56. /// <summary>Delegate for overriding Unity's default input system. Returns the value
  57. /// of the named axis.</summary>
  58. public delegate float AxisInputDelegate(string axisName);
  59. /// <summary>Delegate for overriding Unity's default input system.
  60. /// If you set this, then your delegate will be called instead of
  61. /// System.Input.GetAxis(axisName) whenever in-game user input is needed.</summary>
  62. public static AxisInputDelegate GetInputAxis = UnityEngine.Input.GetAxis;
  63. /// <summary>
  64. /// If non-negative, cinemachine will update with this uniform delta time.
  65. /// Usage is for timelines in manual update mode.
  66. /// </summary>
  67. public static float UniformDeltaTimeOverride = -1;
  68. /// <summary>
  69. /// Replacement for Time.deltaTime, taking UniformDeltaTimeOverride into account.
  70. /// </summary>
  71. public static float DeltaTime => UniformDeltaTimeOverride >= 0 ? UniformDeltaTimeOverride : Time.deltaTime;
  72. /// <summary>
  73. /// If non-negative, cinemachine willuse this value whenever it wants current game time.
  74. /// Usage is for master timelines in manual update mode, for deterministic behaviour.
  75. /// </summary>
  76. public static float CurrentTimeOverride = -1;
  77. /// <summary>
  78. /// Replacement for Time.time, taking CurrentTimeTimeOverride into account.
  79. /// </summary>
  80. public static float CurrentTime => CurrentTimeOverride >= 0 ? CurrentTimeOverride : Time.time;
  81. /// <summary>
  82. /// Delegate for overriding a blend that is about to be applied to a transition.
  83. /// A handler can either return the default blend, or a new blend specific to
  84. /// current conditions.
  85. /// </summary>
  86. /// <param name="fromVcam">The outgoing virtual camera</param>
  87. /// <param name="toVcam">Yhe incoming virtual camera</param>
  88. /// <param name="defaultBlend">The blend that would normally be applied</param>
  89. /// <param name="owner">The context in which the blend is taking place.
  90. /// Can be a CinemachineBrain, or CinemachineStateDrivenCamera, or other manager
  91. /// object that can initiate a blend</param>
  92. /// <returns>The blend definition to use for this transition.</returns>
  93. public delegate CinemachineBlendDefinition GetBlendOverrideDelegate(
  94. ICinemachineCamera fromVcam, ICinemachineCamera toVcam,
  95. CinemachineBlendDefinition defaultBlend,
  96. MonoBehaviour owner);
  97. /// <summary>
  98. /// Delegate for overriding a blend that is about to be applied to a transition.
  99. /// A handler can either return the default blend, or a new blend specific to
  100. /// current conditions.
  101. /// </summary>
  102. public static GetBlendOverrideDelegate GetBlendOverride;
  103. /// <summary>This event will fire after a brain updates its Camera</summary>
  104. public static CinemachineBrain.BrainEvent CameraUpdatedEvent = new CinemachineBrain.BrainEvent();
  105. /// <summary>This event will fire after a brain updates its Camera</summary>
  106. public static CinemachineBrain.BrainEvent CameraCutEvent = new CinemachineBrain.BrainEvent();
  107. /// <summary>List of all active CinemachineBrains.</summary>
  108. private List<CinemachineBrain> mActiveBrains = new List<CinemachineBrain>();
  109. /// <summary>Access the array of active CinemachineBrains in the scene</summary>
  110. public int BrainCount { get { return mActiveBrains.Count; } }
  111. /// <summary>Access the array of active CinemachineBrains in the scene
  112. /// without gebnerating garbage</summary>
  113. /// <param name="index">Index of the brain to access, range 0-BrainCount</param>
  114. /// <returns>The brain at the specified index</returns>
  115. public CinemachineBrain GetActiveBrain(int index)
  116. {
  117. return mActiveBrains[index];
  118. }
  119. /// <summary>Called when a CinemachineBrain is enabled.</summary>
  120. internal void AddActiveBrain(CinemachineBrain brain)
  121. {
  122. // First remove it, just in case it's being added twice
  123. RemoveActiveBrain(brain);
  124. mActiveBrains.Insert(0, brain);
  125. }
  126. /// <summary>Called when a CinemachineBrain is disabled.</summary>
  127. internal void RemoveActiveBrain(CinemachineBrain brain)
  128. {
  129. mActiveBrains.Remove(brain);
  130. }
  131. /// <summary>List of all active ICinemachineCameras.</summary>
  132. private List<CinemachineVirtualCameraBase> mActiveCameras = new List<CinemachineVirtualCameraBase>();
  133. /// <summary>
  134. /// List of all active Cinemachine Virtual Cameras for all brains.
  135. /// This list is kept sorted by priority.
  136. /// </summary>
  137. public int VirtualCameraCount { get { return mActiveCameras.Count; } }
  138. /// <summary>Access the array of active ICinemachineCamera in the scene
  139. /// without gebnerating garbage</summary>
  140. /// <param name="index">Index of the camera to access, range 0-VirtualCameraCount</param>
  141. /// <returns>The virtual camera at the specified index</returns>
  142. public CinemachineVirtualCameraBase GetVirtualCamera(int index)
  143. {
  144. return mActiveCameras[index];
  145. }
  146. /// <summary>Called when a Cinemachine Virtual Camera is enabled.</summary>
  147. internal void AddActiveCamera(CinemachineVirtualCameraBase vcam)
  148. {
  149. // Bring it to the top of the list
  150. RemoveActiveCamera(vcam);
  151. // Keep list sorted by priority
  152. int insertIndex;
  153. for (insertIndex = 0; insertIndex < mActiveCameras.Count; ++insertIndex)
  154. if (vcam.Priority >= mActiveCameras[insertIndex].Priority)
  155. break;
  156. mActiveCameras.Insert(insertIndex, vcam);
  157. }
  158. /// <summary>Called when a Cinemachine Virtual Camera is disabled.</summary>
  159. internal void RemoveActiveCamera(CinemachineVirtualCameraBase vcam)
  160. {
  161. mActiveCameras.Remove(vcam);
  162. }
  163. /// <summary>Called when a Cinemachine Virtual Camera is destroyed.</summary>
  164. internal void CameraDestroyed(CinemachineVirtualCameraBase vcam)
  165. {
  166. if (mActiveCameras.Contains(vcam))
  167. mActiveCameras.Remove(vcam);
  168. if (mUpdateStatus != null && mUpdateStatus.ContainsKey(vcam))
  169. mUpdateStatus.Remove(vcam);
  170. }
  171. // Registry of all vcams that are present, active or not
  172. private List<List<CinemachineVirtualCameraBase>> mAllCameras
  173. = new List<List<CinemachineVirtualCameraBase>>();
  174. /// <summary>Called when a vcam is enabled.</summary>
  175. internal void CameraEnabled(CinemachineVirtualCameraBase vcam)
  176. {
  177. int parentLevel = 0;
  178. for (ICinemachineCamera p = vcam.ParentCamera; p != null; p = p.ParentCamera)
  179. ++parentLevel;
  180. while (mAllCameras.Count <= parentLevel)
  181. mAllCameras.Add(new List<CinemachineVirtualCameraBase>());
  182. mAllCameras[parentLevel].Add(vcam);
  183. }
  184. /// <summary>Called when a vcam is disabled.</summary>
  185. internal void CameraDisabled(CinemachineVirtualCameraBase vcam)
  186. {
  187. for (int i = 0; i < mAllCameras.Count; ++i)
  188. mAllCameras[i].Remove(vcam);
  189. if (mRoundRobinVcamLastFrame == vcam)
  190. mRoundRobinVcamLastFrame = null;
  191. }
  192. CinemachineVirtualCameraBase mRoundRobinVcamLastFrame = null;
  193. static float mLastUpdateTime;
  194. static int FixedFrameCount { get; set; } // Current fixed frame count
  195. /// <summary>Update all the active vcams in the scene, in the correct dependency order.</summary>
  196. internal void UpdateAllActiveVirtualCameras(int layerMask, Vector3 worldUp, float deltaTime)
  197. {
  198. // Setup for roundRobin standby updating
  199. var filter = CurrentUpdateFilter;
  200. bool canUpdateStandby = (filter != UpdateFilter.SmartFixed); // never in smart fixed
  201. CinemachineVirtualCameraBase currentRoundRobin = mRoundRobinVcamLastFrame;
  202. // Update the fixed frame count
  203. float now = CinemachineCore.CurrentTime;
  204. if (now != mLastUpdateTime)
  205. {
  206. mLastUpdateTime = now;
  207. if ((filter & ~UpdateFilter.Smart) == UpdateFilter.Fixed)
  208. ++FixedFrameCount;
  209. }
  210. // Update the leaf-most cameras first
  211. for (int i = mAllCameras.Count-1; i >= 0; --i)
  212. {
  213. var sublist = mAllCameras[i];
  214. for (int j = sublist.Count - 1; j >= 0; --j)
  215. {
  216. var vcam = sublist[j];
  217. if (canUpdateStandby && vcam == mRoundRobinVcamLastFrame)
  218. currentRoundRobin = null; // update the next roundrobin candidate
  219. if (vcam == null)
  220. {
  221. sublist.RemoveAt(j);
  222. continue; // deleted
  223. }
  224. if (vcam.m_StandbyUpdate == CinemachineVirtualCameraBase.StandbyUpdateMode.Always
  225. || IsLive(vcam))
  226. {
  227. // Skip this vcam if it's not on the layer mask
  228. if (((1 << vcam.gameObject.layer) & layerMask) != 0)
  229. UpdateVirtualCamera(vcam, worldUp, deltaTime);
  230. }
  231. else if (currentRoundRobin == null
  232. && mRoundRobinVcamLastFrame != vcam
  233. && canUpdateStandby
  234. && vcam.m_StandbyUpdate != CinemachineVirtualCameraBase.StandbyUpdateMode.Never
  235. && vcam.isActiveAndEnabled)
  236. {
  237. // Do the round-robin update
  238. CurrentUpdateFilter &= ~UpdateFilter.Smart; // force it
  239. UpdateVirtualCamera(vcam, worldUp, deltaTime);
  240. CurrentUpdateFilter = filter;
  241. currentRoundRobin = vcam;
  242. }
  243. }
  244. }
  245. // Did we manage to update a roundrobin?
  246. if (canUpdateStandby)
  247. {
  248. if (currentRoundRobin == mRoundRobinVcamLastFrame)
  249. currentRoundRobin = null; // take the first candidate
  250. mRoundRobinVcamLastFrame = currentRoundRobin;
  251. }
  252. }
  253. /// <summary>
  254. /// Update a single Cinemachine Virtual Camera if and only if it
  255. /// hasn't already been updated this frame. Always update vcams via this method.
  256. /// Calling this more than once per frame for the same camera will have no effect.
  257. /// </summary>
  258. internal void UpdateVirtualCamera(
  259. CinemachineVirtualCameraBase vcam, Vector3 worldUp, float deltaTime)
  260. {
  261. if (vcam == null)
  262. return;
  263. bool isSmartUpdate = (CurrentUpdateFilter & UpdateFilter.Smart) == UpdateFilter.Smart;
  264. UpdateTracker.UpdateClock updateClock
  265. = (UpdateTracker.UpdateClock)(CurrentUpdateFilter & ~UpdateFilter.Smart);
  266. // If we're in smart update mode and the target moved, then we must examine
  267. // how the target has been moving recently in order to figure out whether to
  268. // update now
  269. if (isSmartUpdate)
  270. {
  271. Transform updateTarget = GetUpdateTarget(vcam);
  272. if (updateTarget == null)
  273. return; // vcam deleted
  274. if (UpdateTracker.GetPreferredUpdate(updateTarget) != updateClock)
  275. return; // wrong clock
  276. }
  277. // Have we already been updated this frame?
  278. if (mUpdateStatus == null)
  279. mUpdateStatus = new Dictionary<CinemachineVirtualCameraBase, UpdateStatus>();
  280. if (!mUpdateStatus.TryGetValue(vcam, out UpdateStatus status))
  281. {
  282. status = new UpdateStatus
  283. {
  284. lastUpdateDeltaTime = -2,
  285. lastUpdateMode = UpdateTracker.UpdateClock.Late,
  286. lastUpdateFrame = Time.frameCount + 2, // so that frameDelta ends up negative
  287. lastUpdateFixedFrame = FixedFrameCount + 2
  288. };
  289. mUpdateStatus.Add(vcam, status);
  290. }
  291. int frameDelta = (updateClock == UpdateTracker.UpdateClock.Late)
  292. ? Time.frameCount - status.lastUpdateFrame
  293. : FixedFrameCount - status.lastUpdateFixedFrame;
  294. if (deltaTime >= 0)
  295. {
  296. if (frameDelta == 0 && status.lastUpdateMode == updateClock
  297. && status.lastUpdateDeltaTime == deltaTime)
  298. return; // already updated
  299. if (frameDelta > 0)
  300. deltaTime *= frameDelta; // try to catch up if multiple frames
  301. }
  302. //Debug.Log((vcam.ParentCamera == null ? "" : vcam.ParentCamera.Name + ".") + vcam.Name + ": frame " + Time.frameCount + "/" + status.lastUpdateFixedFrame + ", " + CurrentUpdateFilter + ", deltaTime = " + deltaTime);
  303. vcam.InternalUpdateCameraState(worldUp, deltaTime);
  304. status.lastUpdateFrame = Time.frameCount;
  305. status.lastUpdateFixedFrame = FixedFrameCount;
  306. status.lastUpdateMode = updateClock;
  307. status.lastUpdateDeltaTime = deltaTime;
  308. }
  309. class UpdateStatus
  310. {
  311. public int lastUpdateFrame;
  312. public int lastUpdateFixedFrame;
  313. public UpdateTracker.UpdateClock lastUpdateMode;
  314. public float lastUpdateDeltaTime;
  315. }
  316. Dictionary<CinemachineVirtualCameraBase, UpdateStatus> mUpdateStatus;
  317. [RuntimeInitializeOnLoadMethod]
  318. static void InitializeModule()
  319. {
  320. CinemachineCore.Instance.mUpdateStatus = new Dictionary<CinemachineVirtualCameraBase, UpdateStatus>();
  321. }
  322. /// <summary>Internal use only</summary>
  323. internal enum UpdateFilter
  324. {
  325. Fixed = UpdateTracker.UpdateClock.Fixed,
  326. Late = UpdateTracker.UpdateClock.Late,
  327. Smart = 8, // meant to be or'ed with the others
  328. SmartFixed = Smart | Fixed,
  329. SmartLate = Smart | Late
  330. }
  331. internal UpdateFilter CurrentUpdateFilter { get; set; }
  332. private static Transform GetUpdateTarget(CinemachineVirtualCameraBase vcam)
  333. {
  334. if (vcam == null || vcam.gameObject == null)
  335. return null;
  336. Transform target = vcam.LookAt;
  337. if (target != null)
  338. return target;
  339. target = vcam.Follow;
  340. if (target != null)
  341. return target;
  342. // If no target, use the vcam itself
  343. return vcam.transform;
  344. }
  345. /// <summary>Internal use only - inspector</summary>
  346. internal UpdateTracker.UpdateClock GetVcamUpdateStatus(CinemachineVirtualCameraBase vcam)
  347. {
  348. UpdateStatus status;
  349. if (mUpdateStatus == null || !mUpdateStatus.TryGetValue(vcam, out status))
  350. return UpdateTracker.UpdateClock.Late;
  351. return status.lastUpdateMode;
  352. }
  353. /// <summary>
  354. /// Is this virtual camera currently actively controlling any Camera?
  355. /// </summary>
  356. public bool IsLive(ICinemachineCamera vcam)
  357. {
  358. if (vcam != null)
  359. {
  360. for (int i = 0; i < BrainCount; ++i)
  361. {
  362. CinemachineBrain b = GetActiveBrain(i);
  363. if (b != null && b.IsLive(vcam))
  364. return true;
  365. }
  366. }
  367. return false;
  368. }
  369. /// <summary>
  370. /// Signal that the virtual has been activated.
  371. /// If the camera is live, then all CinemachineBrains that are showing it will
  372. /// send an activation event.
  373. /// </summary>
  374. public void GenerateCameraActivationEvent(ICinemachineCamera vcam, ICinemachineCamera vcamFrom)
  375. {
  376. if (vcam != null)
  377. {
  378. for (int i = 0; i < BrainCount; ++i)
  379. {
  380. CinemachineBrain b = GetActiveBrain(i);
  381. if (b != null && b.IsLive(vcam))
  382. b.m_CameraActivatedEvent.Invoke(vcam, vcamFrom);
  383. }
  384. }
  385. }
  386. /// <summary>
  387. /// Signal that the virtual camera's content is discontinuous WRT the previous frame.
  388. /// If the camera is live, then all CinemachineBrains that are showing it will send a cut event.
  389. /// </summary>
  390. public void GenerateCameraCutEvent(ICinemachineCamera vcam)
  391. {
  392. if (vcam != null)
  393. {
  394. for (int i = 0; i < BrainCount; ++i)
  395. {
  396. CinemachineBrain b = GetActiveBrain(i);
  397. if (b != null && b.IsLive(vcam))
  398. {
  399. if (b.m_CameraCutEvent != null)
  400. b.m_CameraCutEvent.Invoke(b);
  401. if (CameraCutEvent != null)
  402. CameraCutEvent.Invoke(b);
  403. }
  404. }
  405. }
  406. }
  407. /// <summary>
  408. /// Try to find a CinemachineBrain to associate with a
  409. /// Cinemachine Virtual Camera. The first CinemachineBrain
  410. /// in which this Cinemachine Virtual Camera is live will be used.
  411. /// If none, then the first active CinemachineBrain with the correct
  412. /// layer filter will be used.
  413. /// Brains with OutputCamera == null will not be returned.
  414. /// Final result may be null.
  415. /// </summary>
  416. /// <param name="vcam">Virtual camera whose potential brain we need.</param>
  417. /// <returns>First CinemachineBrain found that might be
  418. /// appropriate for this vcam, or null</returns>
  419. public CinemachineBrain FindPotentialTargetBrain(CinemachineVirtualCameraBase vcam)
  420. {
  421. if (vcam != null)
  422. {
  423. int numBrains = BrainCount;
  424. for (int i = 0; i < numBrains; ++i)
  425. {
  426. CinemachineBrain b = GetActiveBrain(i);
  427. if (b != null && b.OutputCamera != null && b.IsLive(vcam))
  428. return b;
  429. }
  430. int layer = 1 << vcam.gameObject.layer;
  431. for (int i = 0; i < numBrains; ++i)
  432. {
  433. CinemachineBrain b = GetActiveBrain(i);
  434. if (b != null && b.OutputCamera != null && (b.OutputCamera.cullingMask & layer) != 0)
  435. return b;
  436. }
  437. }
  438. return null;
  439. }
  440. }
  441. }