CinemachineMixingCamera.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. using UnityEngine;
  2. using Cinemachine.Utility;
  3. using System.Collections.Generic;
  4. namespace Cinemachine
  5. {
  6. /// <summary>
  7. /// CinemachineMixingCamera is a "manager camera" that takes on the state of
  8. /// the weighted average of the states of its child virtual cameras.
  9. ///
  10. /// A fixed number of slots are made available for cameras, rather than a dynamic array.
  11. /// We do it this way in order to support weight animation from the Timeline.
  12. /// Timeline cannot animate array elements.
  13. /// </summary>
  14. [DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)]
  15. [DisallowMultipleComponent]
  16. #if UNITY_2018_3_OR_NEWER
  17. [ExecuteAlways]
  18. #else
  19. [ExecuteInEditMode]
  20. #endif
  21. [ExcludeFromPreset]
  22. [AddComponentMenu("Cinemachine/CinemachineMixingCamera")]
  23. [HelpURL(Documentation.BaseURL + "manual/CinemachineMixingCamera.html")]
  24. public class CinemachineMixingCamera : CinemachineVirtualCameraBase
  25. {
  26. /// <summary>The maximum number of tracked cameras. If you want to add
  27. /// more cameras, do it here in the source code, and be sure to add the
  28. /// extra member variables and to make the appropriate changes in
  29. /// GetWeight() and SetWeight().
  30. /// The inspector will figure itself out based on this value.</summary>
  31. public const int MaxCameras = 8;
  32. /// <summary>Weight of the first tracked camera</summary>
  33. [Tooltip("The weight of the first tracked camera")]
  34. public float m_Weight0 = 0.5f;
  35. /// <summary>Weight of the second tracked camera</summary>
  36. [Tooltip("The weight of the second tracked camera")]
  37. public float m_Weight1 = 0.5f;
  38. /// <summary>Weight of the third tracked camera</summary>
  39. [Tooltip("The weight of the third tracked camera")]
  40. public float m_Weight2 = 0.5f;
  41. /// <summary>Weight of the fourth tracked camera</summary>
  42. [Tooltip("The weight of the fourth tracked camera")]
  43. public float m_Weight3 = 0.5f;
  44. /// <summary>Weight of the fifth tracked camera</summary>
  45. [Tooltip("The weight of the fifth tracked camera")]
  46. public float m_Weight4 = 0.5f;
  47. /// <summary>Weight of the sixth tracked camera</summary>
  48. [Tooltip("The weight of the sixth tracked camera")]
  49. public float m_Weight5 = 0.5f;
  50. /// <summary>Weight of the seventh tracked camera</summary>
  51. [Tooltip("The weight of the seventh tracked camera")]
  52. public float m_Weight6 = 0.5f;
  53. /// <summary>Weight of the eighth tracked camera</summary>
  54. [Tooltip("The weight of the eighth tracked camera")]
  55. public float m_Weight7 = 0.5f;
  56. /// <summary>Get the weight of the child at an index.</summary>
  57. /// <param name="index">The child index. Only immediate CinemachineVirtualCameraBase
  58. /// children are counted.</param>
  59. /// <returns>The weight of the camera. Valid only if camera is active and enabled.</returns>
  60. public float GetWeight(int index)
  61. {
  62. switch (index)
  63. {
  64. case 0: return m_Weight0;
  65. case 1: return m_Weight1;
  66. case 2: return m_Weight2;
  67. case 3: return m_Weight3;
  68. case 4: return m_Weight4;
  69. case 5: return m_Weight5;
  70. case 6: return m_Weight6;
  71. case 7: return m_Weight7;
  72. }
  73. Debug.LogError("CinemachineMixingCamera: Invalid index: " + index);
  74. return 0;
  75. }
  76. /// <summary>Set the weight of the child at an index.</summary>
  77. /// <param name="index">The child index. Only immediate CinemachineVirtualCameraBase
  78. /// children are counted.</param>
  79. /// <param name="w">The weight to set. Can be any non-negative number.</param>
  80. public void SetWeight(int index, float w)
  81. {
  82. switch (index)
  83. {
  84. case 0: m_Weight0 = w; return;
  85. case 1: m_Weight1 = w; return;
  86. case 2: m_Weight2 = w; return;
  87. case 3: m_Weight3 = w; return;
  88. case 4: m_Weight4 = w; return;
  89. case 5: m_Weight5 = w; return;
  90. case 6: m_Weight6 = w; return;
  91. case 7: m_Weight7 = w; return;
  92. }
  93. Debug.LogError("CinemachineMixingCamera: Invalid index: " + index);
  94. }
  95. /// <summary>Get the weight of the child CinemachineVirtualCameraBase.</summary>
  96. /// <param name="vcam">The child camera.</param>
  97. /// <returns>The weight of the camera. Valid only if camera is active and enabled.</returns>
  98. public float GetWeight(CinemachineVirtualCameraBase vcam)
  99. {
  100. ValidateListOfChildren();
  101. int index;
  102. if (m_indexMap.TryGetValue(vcam, out index))
  103. return GetWeight(index);
  104. Debug.LogError("CinemachineMixingCamera: Invalid child: "
  105. + ((vcam != null) ? vcam.Name : "(null)"));
  106. return 0;
  107. }
  108. /// <summary>Set the weight of the child CinemachineVirtualCameraBase.</summary>
  109. /// <param name="vcam">The child camera.</param>
  110. /// <param name="w">The weight to set. Can be any non-negative number.</param>
  111. public void SetWeight(CinemachineVirtualCameraBase vcam, float w)
  112. {
  113. ValidateListOfChildren();
  114. int index;
  115. if (m_indexMap.TryGetValue(vcam, out index))
  116. SetWeight(index, w);
  117. else
  118. Debug.LogError("CinemachineMixingCamera: Invalid child: "
  119. + ((vcam != null) ? vcam.Name : "(null)"));
  120. }
  121. /// <summary>Blended camera state</summary>
  122. private CameraState m_State = CameraState.Default;
  123. /// <summary>Get the current "best" child virtual camera, which is nominally
  124. /// the one with the greatest weight.</summary>
  125. private ICinemachineCamera LiveChild { set; get; }
  126. /// <summary>The blended CameraState</summary>
  127. public override CameraState State { get { return m_State; } }
  128. /// <summary>Not used</summary>
  129. override public Transform LookAt { get; set; }
  130. /// <summary>Not used</summary>
  131. override public Transform Follow { get; set; }
  132. /// <summary>This is called to notify the vcam that a target got warped,
  133. /// so that the vcam can update its internal state to make the camera
  134. /// also warp seamlessy.</summary>
  135. /// <param name="target">The object that was warped</param>
  136. /// <param name="positionDelta">The amount the target's position changed</param>
  137. public override void OnTargetObjectWarped(Transform target, Vector3 positionDelta)
  138. {
  139. ValidateListOfChildren();
  140. foreach (var vcam in m_ChildCameras)
  141. vcam.OnTargetObjectWarped(target, positionDelta);
  142. base.OnTargetObjectWarped(target, positionDelta);
  143. }
  144. /// <summary>
  145. /// Force the virtual camera to assume a given position and orientation
  146. /// </summary>
  147. /// <param name="pos">Worldspace pposition to take</param>
  148. /// <param name="rot">Worldspace orientation to take</param>
  149. public override void ForceCameraPosition(Vector3 pos, Quaternion rot)
  150. {
  151. ValidateListOfChildren();
  152. foreach (var vcam in m_ChildCameras)
  153. vcam.ForceCameraPosition(pos, rot);
  154. base.ForceCameraPosition(pos, rot);
  155. }
  156. /// <summary>Makes sure the internal child cache is up to date</summary>
  157. protected override void OnEnable()
  158. {
  159. base.OnEnable();
  160. InvalidateListOfChildren();
  161. }
  162. /// <summary>Makes sure the internal child cache is up to date</summary>
  163. public void OnTransformChildrenChanged()
  164. {
  165. InvalidateListOfChildren();
  166. }
  167. /// <summary>Makes sure the weights are non-negative</summary>
  168. protected override void OnValidate()
  169. {
  170. base.OnValidate();
  171. for (int i = 0; i < MaxCameras; ++i)
  172. SetWeight(i, Mathf.Max(0, GetWeight(i)));
  173. }
  174. /// <summary>Check whether the vcam a live child of this camera.</summary>
  175. /// <param name="vcam">The Virtual Camera to check</param>
  176. /// <param name="dominantChildOnly">If truw, will only return true if this vcam is the dominat live child</param>
  177. /// <returns>True if the vcam is currently actively influencing the state of this vcam</returns>
  178. public override bool IsLiveChild(ICinemachineCamera vcam, bool dominantChildOnly = false)
  179. {
  180. CinemachineVirtualCameraBase[] children = ChildCameras;
  181. for (int i = 0; i < MaxCameras && i < children.Length; ++i)
  182. if ((ICinemachineCamera)children[i] == vcam)
  183. return GetWeight(i) > UnityVectorExtensions.Epsilon && children[i].isActiveAndEnabled;
  184. return false;
  185. }
  186. private CinemachineVirtualCameraBase[] m_ChildCameras;
  187. private Dictionary<CinemachineVirtualCameraBase, int> m_indexMap;
  188. /// <summary>Get the cached list of child cameras.
  189. /// These are just the immediate children in the hierarchy.
  190. /// Note: only the first entries of this list participate in the
  191. /// final blend, up to MaxCameras</summary>
  192. public CinemachineVirtualCameraBase[] ChildCameras
  193. {
  194. get { ValidateListOfChildren(); return m_ChildCameras; }
  195. }
  196. /// <summary>Invalidate the cached list of child cameras.</summary>
  197. protected void InvalidateListOfChildren()
  198. {
  199. m_ChildCameras = null;
  200. m_indexMap = null;
  201. LiveChild = null;
  202. }
  203. /// <summary>Rebuild the cached list of child cameras.</summary>
  204. protected void ValidateListOfChildren()
  205. {
  206. if (m_ChildCameras != null)
  207. return;
  208. m_indexMap = new Dictionary<CinemachineVirtualCameraBase, int>();
  209. List<CinemachineVirtualCameraBase> list = new List<CinemachineVirtualCameraBase>();
  210. CinemachineVirtualCameraBase[] kids
  211. = GetComponentsInChildren<CinemachineVirtualCameraBase>(true);
  212. foreach (CinemachineVirtualCameraBase k in kids)
  213. {
  214. if (k.transform.parent == transform)
  215. {
  216. int index = list.Count;
  217. list.Add(k);
  218. if (index < MaxCameras)
  219. m_indexMap.Add(k, index);
  220. }
  221. }
  222. m_ChildCameras = list.ToArray();
  223. }
  224. /// <summary>Notification that this virtual camera is going live.</summary>
  225. /// <param name="fromCam">The camera being deactivated. May be null.</param>
  226. /// <param name="worldUp">Default world Up, set by the CinemachineBrain</param>
  227. /// <param name="deltaTime">Delta time for time-based effects (ignore if less than or equal to 0)</param>
  228. public override void OnTransitionFromCamera(
  229. ICinemachineCamera fromCam, Vector3 worldUp, float deltaTime)
  230. {
  231. base.OnTransitionFromCamera(fromCam, worldUp, deltaTime);
  232. InvokeOnTransitionInExtensions(fromCam, worldUp, deltaTime);
  233. CinemachineVirtualCameraBase[] children = ChildCameras;
  234. for (int i = 0; i < MaxCameras && i < children.Length; ++i)
  235. {
  236. CinemachineVirtualCameraBase vcam = children[i];
  237. if (vcam.isActiveAndEnabled && GetWeight(i) > UnityVectorExtensions.Epsilon)
  238. vcam.OnTransitionFromCamera(fromCam, worldUp, deltaTime);
  239. }
  240. InternalUpdateCameraState(worldUp, deltaTime);
  241. }
  242. /// <summary>Internal use only. Do not call this methid.
  243. /// Called by CinemachineCore at designated update time
  244. /// so the vcam can position itself and track its targets. This implementation
  245. /// computes and caches the weighted blend of the tracked cameras.</summary>
  246. /// <param name="worldUp">Default world Up, set by the CinemachineBrain</param>
  247. /// <param name="deltaTime">Delta time for time-based effects (ignore if less than 0)</param>
  248. public override void InternalUpdateCameraState(Vector3 worldUp, float deltaTime)
  249. {
  250. CinemachineVirtualCameraBase[] children = ChildCameras;
  251. LiveChild = null;
  252. float highestWeight = 0;
  253. float totalWeight = 0;
  254. for (int i = 0; i < MaxCameras && i < children.Length; ++i)
  255. {
  256. CinemachineVirtualCameraBase vcam = children[i];
  257. if (vcam.isActiveAndEnabled)
  258. {
  259. float weight = Mathf.Max(0, GetWeight(i));
  260. if (weight > UnityVectorExtensions.Epsilon)
  261. {
  262. totalWeight += weight;
  263. if (totalWeight == weight)
  264. m_State = vcam.State;
  265. else
  266. m_State = CameraState.Lerp(m_State, vcam.State, weight / totalWeight);
  267. if (weight > highestWeight)
  268. {
  269. highestWeight = weight;
  270. LiveChild = vcam;
  271. }
  272. }
  273. }
  274. }
  275. InvokePostPipelineStageCallback(
  276. this, CinemachineCore.Stage.Finalize, ref m_State, deltaTime);
  277. }
  278. }
  279. }