CinemachineBlendListCamera.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  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.Generic;
  7. using UnityEngine;
  8. namespace Cinemachine
  9. {
  10. /// <summary>
  11. /// This is a virtual camera "manager" that owns and manages a collection
  12. /// of child Virtual Cameras. When the camera goes live, these child vcams
  13. /// are enabled, one after another, holding each camera for a designated time.
  14. /// Blends between cameras are specified.
  15. /// The last camera is held indefinitely.
  16. /// </summary>
  17. [DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)]
  18. [DisallowMultipleComponent]
  19. [ExecuteAlways]
  20. [ExcludeFromPreset]
  21. [AddComponentMenu("Cinemachine/CinemachineBlendListCamera")]
  22. [HelpURL(Documentation.BaseURL + "manual/CinemachineBlendListCamera.html")]
  23. public class CinemachineBlendListCamera : CinemachineVirtualCameraBase
  24. {
  25. /// <summary>Default object for the camera children to look at (the aim target),
  26. /// if not specified in a child rig. May be empty</summary>
  27. [Tooltip("Default object for the camera children to look at (the aim target), if not "
  28. + "specified in a child camera. May be empty if all of the children define targets of their own.")]
  29. [NoSaveDuringPlay]
  30. [VcamTargetProperty]
  31. public Transform m_LookAt;
  32. /// <summary>Default object for the camera children wants to move with (the body target),
  33. /// if not specified in a child rig. May be empty</summary>
  34. [Tooltip("Default object for the camera children wants to move with (the body target), "
  35. + "if not specified in a child camera. May be empty if all of the children define targets of their own.")]
  36. [NoSaveDuringPlay]
  37. [VcamTargetProperty]
  38. public Transform m_Follow;
  39. /// <summary>When enabled, the current camera and blend will be indicated in the game window, for debugging</summary>
  40. [Tooltip("When enabled, the current child camera and blend will be indicated in the game window, for debugging")]
  41. public bool m_ShowDebugText;
  42. /// <summary>When enabled, the child vcams will cycle indefinitely instead of just stopping at the last one</summary>
  43. [Tooltip("When enabled, the child vcams will cycle indefinitely instead of just stopping at the last one")]
  44. public bool m_Loop;
  45. /// <summary>Internal API for the editor. Do not use this field</summary>
  46. [SerializeField][HideInInspector][NoSaveDuringPlay]
  47. internal CinemachineVirtualCameraBase[] m_ChildCameras;
  48. /// <summary>This represents a single entry in the instrunction list of the BlendListCamera.</summary>
  49. [Serializable]
  50. public struct Instruction
  51. {
  52. /// <summary>The virtual camera to activate when this instruction becomes active</summary>
  53. [Tooltip("The virtual camera to activate when this instruction becomes active")]
  54. public CinemachineVirtualCameraBase m_VirtualCamera;
  55. /// <summary>How long to wait (in seconds) before activating the next virtual camera in the list (if any)</summary>
  56. [Tooltip("How long to wait (in seconds) before activating the next virtual camera in the list (if any)")]
  57. public float m_Hold;
  58. /// <summary>How to blend to the next virtual camera in the list (if any)</summary>
  59. [CinemachineBlendDefinitionProperty]
  60. [Tooltip("How to blend to the next virtual camera in the list (if any)")]
  61. public CinemachineBlendDefinition m_Blend;
  62. };
  63. /// <summary>The set of instructions associating virtual cameras with states.
  64. /// The set of instructions for enabling child cameras</summary>
  65. [Tooltip("The set of instructions for enabling child cameras.")]
  66. public Instruction[] m_Instructions;
  67. /// <summary>Gets a brief debug description of this virtual camera, for use when displayiong debug info</summary>
  68. public override string Description
  69. {
  70. get
  71. {
  72. // Show the active camera and blend
  73. if (mActiveBlend != null)
  74. return mActiveBlend.Description;
  75. ICinemachineCamera vcam = LiveChild;
  76. if (vcam == null)
  77. return "(none)";
  78. var sb = CinemachineDebug.SBFromPool();
  79. sb.Append("["); sb.Append(vcam.Name); sb.Append("]");
  80. string text = sb.ToString();
  81. CinemachineDebug.ReturnToPool(sb);
  82. return text;
  83. }
  84. }
  85. void Reset()
  86. {
  87. m_LookAt = null;
  88. m_Follow = null;
  89. m_ShowDebugText = false;
  90. m_Loop = false;
  91. m_Instructions = null;
  92. m_ChildCameras = null;
  93. }
  94. /// <summary>Get the current "best" child virtual camera, that would be chosen
  95. /// if the State Driven Camera were active.</summary>
  96. public ICinemachineCamera LiveChild { get; set; }
  97. /// <summary>Check whether the vcam a live child of this camera.</summary>
  98. /// <param name="vcam">The Virtual Camera to check</param>
  99. /// <param name="dominantChildOnly">If truw, will only return true if this vcam is the dominat live child</param>
  100. /// <returns>True if the vcam is currently actively influencing the state of this vcam</returns>
  101. public override bool IsLiveChild(ICinemachineCamera vcam, bool dominantChildOnly = false)
  102. {
  103. return vcam == LiveChild || (mActiveBlend != null && mActiveBlend.Uses(vcam));
  104. }
  105. /// <summary>The State of the current live child</summary>
  106. public override CameraState State { get { return m_State; } }
  107. /// <summary>Get the current LookAt target. Returns parent's LookAt if parent
  108. /// is non-null and no specific LookAt defined for this camera</summary>
  109. override public Transform LookAt
  110. {
  111. get { return ResolveLookAt(m_LookAt); }
  112. set { m_LookAt = value; }
  113. }
  114. /// <summary>Get the current Follow target. Returns parent's Follow if parent
  115. /// is non-null and no specific Follow defined for this camera</summary>
  116. override public Transform Follow
  117. {
  118. get { return ResolveFollow(m_Follow); }
  119. set { m_Follow = value; }
  120. }
  121. /// <summary>This is called to notify the vcam that a target got warped,
  122. /// so that the vcam can update its internal state to make the camera
  123. /// also warp seamlessy.</summary>
  124. /// <param name="target">The object that was warped</param>
  125. /// <param name="positionDelta">The amount the target's position changed</param>
  126. public override void OnTargetObjectWarped(Transform target, Vector3 positionDelta)
  127. {
  128. UpdateListOfChildren();
  129. foreach (var vcam in m_ChildCameras)
  130. vcam.OnTargetObjectWarped(target, positionDelta);
  131. base.OnTargetObjectWarped(target, positionDelta);
  132. }
  133. /// <summary>
  134. /// Force the virtual camera to assume a given position and orientation
  135. /// </summary>
  136. /// <param name="pos">Worldspace pposition to take</param>
  137. /// <param name="rot">Worldspace orientation to take</param>
  138. public override void ForceCameraPosition(Vector3 pos, Quaternion rot)
  139. {
  140. UpdateListOfChildren();
  141. foreach (var vcam in m_ChildCameras)
  142. vcam.ForceCameraPosition(pos, rot);
  143. base.ForceCameraPosition(pos, rot);
  144. }
  145. /// <summary>Notification that this virtual camera is going live.</summary>
  146. /// <param name="fromCam">The camera being deactivated. May be null.</param>
  147. /// <param name="worldUp">Default world Up, set by the CinemachineBrain</param>
  148. /// <param name="deltaTime">Delta time for time-based effects (ignore if less than or equal to 0)</param>
  149. public override void OnTransitionFromCamera(
  150. ICinemachineCamera fromCam, Vector3 worldUp, float deltaTime)
  151. {
  152. base.OnTransitionFromCamera(fromCam, worldUp, deltaTime);
  153. InvokeOnTransitionInExtensions(fromCam, worldUp, deltaTime);
  154. mActivationTime = CinemachineCore.CurrentTime;
  155. mCurrentInstruction = 0;
  156. LiveChild = null;
  157. mActiveBlend = null;
  158. m_TransitioningFrom = fromCam;
  159. InternalUpdateCameraState(worldUp, deltaTime);
  160. }
  161. ICinemachineCamera m_TransitioningFrom;
  162. /// <summary>Called by CinemachineCore at designated update time
  163. /// so the vcam can position itself and track its targets. This implementation
  164. /// updates all the children, chooses the best one, and implements any required blending.</summary>
  165. /// <param name="worldUp">Default world Up, set by the CinemachineBrain</param>
  166. /// <param name="deltaTime">Delta time for time-based effects (ignore if less than or equal to 0)</param>
  167. public override void InternalUpdateCameraState(Vector3 worldUp, float deltaTime)
  168. {
  169. if (!PreviousStateIsValid)
  170. {
  171. mCurrentInstruction = -1;
  172. mActiveBlend = null;
  173. }
  174. UpdateListOfChildren();
  175. AdvanceCurrentInstruction(deltaTime);
  176. CinemachineVirtualCameraBase best = null;
  177. if (mCurrentInstruction >= 0 && mCurrentInstruction < m_Instructions.Length)
  178. best = m_Instructions[mCurrentInstruction].m_VirtualCamera;
  179. if (best != null)
  180. {
  181. if (!best.gameObject.activeInHierarchy)
  182. {
  183. best.gameObject.SetActive(true);
  184. best.UpdateCameraState(worldUp, deltaTime);
  185. }
  186. ICinemachineCamera previousCam = LiveChild;
  187. LiveChild = best;
  188. // Are we transitioning cameras?
  189. if (previousCam != LiveChild && LiveChild != null)
  190. {
  191. // Notify incoming camera of transition
  192. LiveChild.OnTransitionFromCamera(previousCam, worldUp, deltaTime);
  193. // Generate Camera Activation event in the brain if live
  194. CinemachineCore.Instance.GenerateCameraActivationEvent(LiveChild, previousCam);
  195. if (previousCam != null)
  196. {
  197. // Create a blend (will be null if a cut)
  198. mActiveBlend = CreateBlend(
  199. previousCam, LiveChild,
  200. m_Instructions[mCurrentInstruction].m_Blend,
  201. mActiveBlend);
  202. // If cutting, generate a camera cut event if live
  203. if (mActiveBlend == null || !mActiveBlend.Uses(previousCam))
  204. CinemachineCore.Instance.GenerateCameraCutEvent(LiveChild);
  205. }
  206. }
  207. }
  208. // Advance the current blend (if any)
  209. if (mActiveBlend != null)
  210. {
  211. mActiveBlend.TimeInBlend += (deltaTime >= 0) ? deltaTime : mActiveBlend.Duration;
  212. if (mActiveBlend.IsComplete)
  213. mActiveBlend = null;
  214. }
  215. if (mActiveBlend != null)
  216. {
  217. mActiveBlend.UpdateCameraState(worldUp, deltaTime);
  218. m_State = mActiveBlend.State;
  219. }
  220. else if (LiveChild != null)
  221. {
  222. if (m_TransitioningFrom != null)
  223. LiveChild.OnTransitionFromCamera(m_TransitioningFrom, worldUp, deltaTime);
  224. m_State = LiveChild.State;
  225. }
  226. m_TransitioningFrom = null;
  227. InvokePostPipelineStageCallback(
  228. this, CinemachineCore.Stage.Finalize, ref m_State, deltaTime);
  229. PreviousStateIsValid = true;
  230. }
  231. /// <summary>Makes sure the internal child cache is up to date</summary>
  232. protected override void OnEnable()
  233. {
  234. base.OnEnable();
  235. InvalidateListOfChildren();
  236. LiveChild = null;
  237. mActiveBlend = null;
  238. CinemachineDebug.OnGUIHandlers -= OnGuiHandler;
  239. CinemachineDebug.OnGUIHandlers += OnGuiHandler;
  240. }
  241. /// <summary>
  242. /// Uninstall the GUI handler
  243. /// </summary>
  244. protected override void OnDisable()
  245. {
  246. base.OnDisable();
  247. CinemachineDebug.OnGUIHandlers -= OnGuiHandler;
  248. }
  249. /// <summary>Makes sure the internal child cache is up to date</summary>
  250. void OnTransformChildrenChanged()
  251. {
  252. InvalidateListOfChildren();
  253. }
  254. /// Will only be called if Unity Editor - never in build
  255. private void OnGuiHandler()
  256. {
  257. #if CINEMACHINE_UNITY_IMGUI
  258. if (!m_ShowDebugText)
  259. CinemachineDebug.ReleaseScreenPos(this);
  260. else
  261. {
  262. var sb = CinemachineDebug.SBFromPool();
  263. sb.Append(Name); sb.Append(": "); sb.Append(Description);
  264. string text = sb.ToString();
  265. Rect r = CinemachineDebug.GetScreenPos(this, text, GUI.skin.box);
  266. GUI.Label(r, text, GUI.skin.box);
  267. CinemachineDebug.ReturnToPool(sb);
  268. }
  269. #endif
  270. }
  271. CameraState m_State = CameraState.Default;
  272. /// <summary>The list of child cameras. These are just the immediate children in the hierarchy.</summary>
  273. public CinemachineVirtualCameraBase[] ChildCameras { get { UpdateListOfChildren(); return m_ChildCameras; }}
  274. /// <summary>Is there a blend in progress?</summary>
  275. public bool IsBlending { get { return mActiveBlend != null; } }
  276. /// <summary>The time at which the current instruction went live</summary>
  277. float mActivationTime = -1;
  278. int mCurrentInstruction = 0;
  279. private CinemachineBlend mActiveBlend = null;
  280. void InvalidateListOfChildren() { m_ChildCameras = null; LiveChild = null; }
  281. void UpdateListOfChildren()
  282. {
  283. if (m_ChildCameras != null)
  284. return;
  285. List<CinemachineVirtualCameraBase> list = new List<CinemachineVirtualCameraBase>();
  286. CinemachineVirtualCameraBase[] kids = GetComponentsInChildren<CinemachineVirtualCameraBase>(true);
  287. foreach (CinemachineVirtualCameraBase k in kids)
  288. if (k.transform.parent == transform)
  289. list.Add(k);
  290. m_ChildCameras = list.ToArray();
  291. ValidateInstructions();
  292. }
  293. /// <summary>Internal API for the inspector editor.</summary>
  294. /// // GML todo: make this private, part of UpdateListOfChildren()
  295. internal void ValidateInstructions()
  296. {
  297. if (m_Instructions == null)
  298. m_Instructions = Array.Empty<Instruction>();
  299. for (int i = 0; i < m_Instructions.Length; ++i)
  300. {
  301. if (m_Instructions[i].m_VirtualCamera != null
  302. && m_Instructions[i].m_VirtualCamera.transform.parent != transform)
  303. {
  304. m_Instructions[i].m_VirtualCamera = null;
  305. }
  306. }
  307. mActiveBlend = null;
  308. }
  309. private void AdvanceCurrentInstruction(float deltaTime)
  310. {
  311. if (m_ChildCameras == null || m_ChildCameras.Length == 0
  312. || mActivationTime < 0 || m_Instructions.Length == 0)
  313. {
  314. mActivationTime = -1;
  315. mCurrentInstruction = -1;
  316. mActiveBlend = null;
  317. return;
  318. }
  319. float now = CinemachineCore.CurrentTime;
  320. if (mCurrentInstruction < 0 || deltaTime < 0)
  321. {
  322. mActivationTime = now;
  323. mCurrentInstruction = 0;
  324. }
  325. if (mCurrentInstruction > m_Instructions.Length - 1)
  326. {
  327. mActivationTime = now;
  328. mCurrentInstruction = m_Instructions.Length - 1;
  329. }
  330. var holdTime = m_Instructions[mCurrentInstruction].m_Hold
  331. + m_Instructions[mCurrentInstruction].m_Blend.BlendTime;
  332. var minHold = mCurrentInstruction < m_Instructions.Length - 1 || m_Loop
  333. ? 0 : float.MaxValue;
  334. if (now - mActivationTime > Mathf.Max(minHold, holdTime))
  335. {
  336. mActivationTime = now;
  337. ++mCurrentInstruction;
  338. if (m_Loop && mCurrentInstruction == m_Instructions.Length)
  339. mCurrentInstruction = 0;
  340. }
  341. }
  342. }
  343. }