CinemachineBlend.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. using Cinemachine.Utility;
  2. using System;
  3. using UnityEngine;
  4. namespace Cinemachine
  5. {
  6. /// <summary>
  7. /// Describes a blend between 2 Cinemachine Virtual Cameras, and holds the
  8. /// current state of the blend.
  9. /// </summary>
  10. public class CinemachineBlend
  11. {
  12. /// <summary>First camera in the blend</summary>
  13. public ICinemachineCamera CamA { get; set; }
  14. /// <summary>Second camera in the blend</summary>
  15. public ICinemachineCamera CamB { get; set; }
  16. /// <summary>The curve that describes the way the blend transitions over time
  17. /// from the first camera to the second. X-axis is normalized time (0...1) over which
  18. /// the blend takes place and Y axis is blend weight (0..1)</summary>
  19. public AnimationCurve BlendCurve { get; set; }
  20. /// <summary>The current time relative to the start of the blend</summary>
  21. public float TimeInBlend { get; set; }
  22. /// <summary>The current weight of the blend. This is an evaluation of the
  23. /// BlendCurve at the current time relative to the start of the blend.
  24. /// 0 means camA, 1 means camB.</summary>
  25. public float BlendWeight
  26. {
  27. get
  28. {
  29. if (BlendCurve == null || BlendCurve.length < 2 || IsComplete)
  30. return 1;
  31. return Mathf.Clamp01(BlendCurve.Evaluate(TimeInBlend / Duration));
  32. }
  33. }
  34. /// <summary>Validity test for the blend. True if either camera is defined.</summary>
  35. public bool IsValid { get { return ((CamA != null && CamA.IsValid) || (CamB != null && CamB.IsValid)); } }
  36. /// <summary>Duration in seconds of the blend.</summary>
  37. public float Duration { get; set; }
  38. /// <summary>True if the time relative to the start of the blend is greater
  39. /// than or equal to the blend duration</summary>
  40. public bool IsComplete { get { return TimeInBlend >= Duration || !IsValid; } }
  41. /// <summary>Text description of the blend, for debugging</summary>
  42. public string Description
  43. {
  44. get
  45. {
  46. var sb = CinemachineDebug.SBFromPool();
  47. if (CamB == null || !CamB.IsValid)
  48. sb.Append("(none)");
  49. else
  50. {
  51. sb.Append("[");
  52. sb.Append(CamB.Name);
  53. sb.Append("]");
  54. }
  55. sb.Append(" ");
  56. sb.Append((int)(BlendWeight * 100f));
  57. sb.Append("% from ");
  58. if (CamA == null || !CamA.IsValid)
  59. sb.Append("(none)");
  60. else
  61. {
  62. sb.Append("[");
  63. sb.Append(CamA.Name);
  64. sb.Append("]");
  65. }
  66. string text = sb.ToString();
  67. CinemachineDebug.ReturnToPool(sb);
  68. return text;
  69. }
  70. }
  71. /// <summary>Does the blend use a specific Cinemachine Virtual Camera?</summary>
  72. /// <param name="cam">The camera to test</param>
  73. /// <returns>True if the camera is involved in the blend</returns>
  74. public bool Uses(ICinemachineCamera cam)
  75. {
  76. if (cam == CamA || cam == CamB)
  77. return true;
  78. BlendSourceVirtualCamera b = CamA as BlendSourceVirtualCamera;
  79. if (b != null && b.Blend.Uses(cam))
  80. return true;
  81. b = CamB as BlendSourceVirtualCamera;
  82. if (b != null && b.Blend.Uses(cam))
  83. return true;
  84. return false;
  85. }
  86. /// <summary>Construct a blend</summary>
  87. /// <param name="a">First camera</param>
  88. /// <param name="b">Second camera</param>
  89. /// <param name="curve">Blend curve</param>
  90. /// <param name="duration">Duration of the blend, in seconds</param>
  91. /// <param name="t">Current time in blend, relative to the start of the blend</param>
  92. public CinemachineBlend(
  93. ICinemachineCamera a, ICinemachineCamera b, AnimationCurve curve, float duration, float t)
  94. {
  95. CamA = a;
  96. CamB = b;
  97. BlendCurve = curve;
  98. TimeInBlend = t;
  99. Duration = duration;
  100. }
  101. /// <summary>Make sure the source cameras get updated.</summary>
  102. /// <param name="worldUp">Default world up. Individual vcams may modify this</param>
  103. /// <param name="deltaTime">Time increment used for calculating time-based behaviours (e.g. damping)</param>
  104. public void UpdateCameraState(Vector3 worldUp, float deltaTime)
  105. {
  106. // Make sure both cameras have been updated (they are not necessarily
  107. // enabled, and only enabled cameras get updated automatically
  108. // every frame)
  109. if (CamA != null && CamA.IsValid)
  110. CamA.UpdateCameraState(worldUp, deltaTime);
  111. if (CamB != null && CamB.IsValid)
  112. CamB.UpdateCameraState(worldUp, deltaTime);
  113. }
  114. /// <summary>Compute the blended CameraState for the current time in the blend.</summary>
  115. public CameraState State
  116. {
  117. get
  118. {
  119. if (CamA == null || !CamA.IsValid)
  120. {
  121. if (CamB == null || !CamB.IsValid)
  122. return CameraState.Default;
  123. return CamB.State;
  124. }
  125. if (CamB == null || !CamB.IsValid)
  126. return CamA.State;
  127. return CameraState.Lerp(CamA.State, CamB.State, BlendWeight);
  128. }
  129. }
  130. }
  131. /// <summary>Definition of a Camera blend. This struct holds the information
  132. /// necessary to generate a suitable AnimationCurve for a Cinemachine Blend.</summary>
  133. [Serializable]
  134. [DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)]
  135. public struct CinemachineBlendDefinition
  136. {
  137. /// <summary>Supported predefined shapes for the blend curve.</summary>
  138. [DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)]
  139. public enum Style
  140. {
  141. /// <summary>Zero-length blend</summary>
  142. Cut,
  143. /// <summary>S-shaped curve, giving a gentle and smooth transition</summary>
  144. EaseInOut,
  145. /// <summary>Linear out of the outgoing shot, and easy into the incoming</summary>
  146. EaseIn,
  147. /// <summary>Easy out of the outgoing shot, and linear into the incoming</summary>
  148. EaseOut,
  149. /// <summary>Easy out of the outgoing, and hard into the incoming</summary>
  150. HardIn,
  151. /// <summary>Hard out of the outgoing, and easy into the incoming</summary>
  152. HardOut,
  153. /// <summary>Linear blend. Mechanical-looking.</summary>
  154. Linear,
  155. /// <summary>Custom blend curve.</summary>
  156. Custom
  157. };
  158. /// <summary>The shape of the blend curve.</summary>
  159. [Tooltip("Shape of the blend curve")]
  160. public Style m_Style;
  161. /// <summary>The duration (in seconds) of the blend, if not a cut.
  162. /// If style is a cut, then this value is ignored.</summary>
  163. [Tooltip("Duration of the blend, in seconds")]
  164. public float m_Time;
  165. /// <summary>
  166. /// Get the duration of the blend, in seconds. Will return 0 if blend style is a cut.
  167. /// </summary>
  168. public float BlendTime { get { return m_Style == Style.Cut ? 0 : m_Time; } }
  169. /// <summary>Constructor</summary>
  170. /// <param name="style">The shape of the blend curve.</param>
  171. /// <param name="time">The duration (in seconds) of the blend</param>
  172. public CinemachineBlendDefinition(Style style, float time)
  173. {
  174. m_Style = style;
  175. m_Time = time;
  176. m_CustomCurve = null;
  177. }
  178. /// <summary>
  179. /// A user-defined AnimationCurve, used only if style is Custom.
  180. /// Curve MUST be normalized, i.e. time range [0...1], value range [0...1].
  181. /// </summary>
  182. public AnimationCurve m_CustomCurve;
  183. static AnimationCurve[] sStandardCurves;
  184. void CreateStandardCurves()
  185. {
  186. sStandardCurves = new AnimationCurve[(int)Style.Custom];
  187. sStandardCurves[(int)Style.Cut] = null;
  188. sStandardCurves[(int)Style.EaseInOut] = AnimationCurve.EaseInOut(0f, 0f, 1, 1f);
  189. sStandardCurves[(int)Style.EaseIn] = AnimationCurve.Linear(0f, 0f, 1, 1f);
  190. Keyframe[] keys = sStandardCurves[(int)Style.EaseIn].keys;
  191. keys[0].outTangent = 1.4f;
  192. keys[1].inTangent = 0;
  193. sStandardCurves[(int)Style.EaseIn].keys = keys;
  194. sStandardCurves[(int)Style.EaseOut] = AnimationCurve.Linear(0f, 0f, 1, 1f);
  195. keys = sStandardCurves[(int)Style.EaseOut].keys;
  196. keys[0].outTangent = 0;
  197. keys[1].inTangent = 1.4f;
  198. sStandardCurves[(int)Style.EaseOut].keys = keys;
  199. sStandardCurves[(int)Style.HardIn] = AnimationCurve.Linear(0f, 0f, 1, 1f);
  200. keys = sStandardCurves[(int)Style.HardIn].keys;
  201. keys[0].outTangent = 0;
  202. keys[1].inTangent = 3f;
  203. sStandardCurves[(int)Style.HardIn].keys = keys;
  204. sStandardCurves[(int)Style.HardOut] = AnimationCurve.Linear(0f, 0f, 1, 1f);
  205. keys = sStandardCurves[(int)Style.HardOut].keys;
  206. keys[0].outTangent = 3f;
  207. keys[1].inTangent = 0;
  208. sStandardCurves[(int)Style.HardOut].keys = keys;
  209. sStandardCurves[(int)Style.Linear] = AnimationCurve.Linear(0f, 0f, 1, 1f);
  210. }
  211. /// <summary>
  212. /// A normalized AnimationCurve specifying the interpolation curve
  213. /// for this camera blend. Y-axis values must be in range [0,1] (internally clamped
  214. /// within Blender) and time must be in range of [0, 1].
  215. /// </summary>
  216. public AnimationCurve BlendCurve
  217. {
  218. get
  219. {
  220. if (m_Style == Style.Custom)
  221. {
  222. if (m_CustomCurve == null)
  223. m_CustomCurve = AnimationCurve.EaseInOut(0f, 0f, 1, 1f);
  224. return m_CustomCurve;
  225. }
  226. if (sStandardCurves == null)
  227. CreateStandardCurves();
  228. return sStandardCurves[(int)m_Style];
  229. }
  230. }
  231. }
  232. /// <summary>
  233. /// Point source for blending. It's not really a virtual camera, but takes
  234. /// a CameraState and exposes it as a virtual camera for the purposes of blending.
  235. /// </summary>
  236. internal class StaticPointVirtualCamera : ICinemachineCamera
  237. {
  238. public StaticPointVirtualCamera(CameraState state, string name) { State = state; Name = name; }
  239. public void SetState(CameraState state) { State = state; }
  240. public string Name { get; private set; }
  241. public string Description { get { return ""; }}
  242. public int Priority { get; set; }
  243. public Transform LookAt { get; set; }
  244. public Transform Follow { get; set; }
  245. public CameraState State { get; private set; }
  246. public GameObject VirtualCameraGameObject { get { return null; } }
  247. public bool IsValid { get { return true; } }
  248. public ICinemachineCamera ParentCamera { get { return null; } }
  249. public bool IsLiveChild(ICinemachineCamera vcam, bool dominantChildOnly = false) { return false; }
  250. public void UpdateCameraState(Vector3 worldUp, float deltaTime) {}
  251. public void InternalUpdateCameraState(Vector3 worldUp, float deltaTime) {}
  252. public void OnTransitionFromCamera(ICinemachineCamera fromCam, Vector3 worldUp, float deltaTime) {}
  253. public void OnTargetObjectWarped(Transform target, Vector3 positionDelta) {}
  254. }
  255. /// <summary>
  256. /// Blend result source for blending. This exposes a CinemachineBlend object
  257. /// as an ersatz virtual camera for the purposes of blending. This achieves the purpose
  258. /// of blending the result oif a blend.
  259. /// </summary>
  260. internal class BlendSourceVirtualCamera : ICinemachineCamera
  261. {
  262. public BlendSourceVirtualCamera(CinemachineBlend blend) { Blend = blend; }
  263. public CinemachineBlend Blend { get; set; }
  264. public string Name { get { return "Mid-blend"; }}
  265. public string Description { get { return Blend == null ? "(null)" : Blend.Description; }}
  266. public int Priority { get; set; }
  267. public Transform LookAt { get; set; }
  268. public Transform Follow { get; set; }
  269. public CameraState State { get; private set; }
  270. public GameObject VirtualCameraGameObject { get { return null; } }
  271. public bool IsValid { get { return Blend != null && Blend.IsValid; } }
  272. public ICinemachineCamera ParentCamera { get { return null; } }
  273. public bool IsLiveChild(ICinemachineCamera vcam, bool dominantChildOnly = false)
  274. { return Blend != null && (vcam == Blend.CamA || vcam == Blend.CamB); }
  275. public CameraState CalculateNewState(float deltaTime) { return State; }
  276. public void UpdateCameraState(Vector3 worldUp, float deltaTime)
  277. {
  278. if (Blend != null)
  279. {
  280. Blend.UpdateCameraState(worldUp, deltaTime);
  281. State = Blend.State;
  282. }
  283. }
  284. public void InternalUpdateCameraState(Vector3 worldUp, float deltaTime) {}
  285. public void OnTransitionFromCamera(ICinemachineCamera fromCam, Vector3 worldUp, float deltaTime) {}
  286. public void OnTargetObjectWarped(Transform target, Vector3 positionDelta) {}
  287. }
  288. }