CinemachineFreeLook.cs 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836
  1. using UnityEngine;
  2. using Cinemachine.Utility;
  3. using UnityEngine.Serialization;
  4. using System;
  5. namespace Cinemachine
  6. {
  7. /// <summary>
  8. /// A Cinemachine Camera geared towards a 3rd person camera experience.
  9. /// The camera orbits around its subject with three separate camera rigs defining
  10. /// rings around the target. Each rig has its own radius, height offset, composer,
  11. /// and lens settings.
  12. /// Depending on the camera's position along the spline connecting these three rigs,
  13. /// these settings are interpolated to give the final camera position and state.
  14. /// </summary>
  15. [DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)]
  16. [DisallowMultipleComponent]
  17. #if UNITY_2018_3_OR_NEWER
  18. [ExecuteAlways]
  19. #else
  20. [ExecuteInEditMode]
  21. #endif
  22. [ExcludeFromPreset]
  23. [AddComponentMenu("Cinemachine/CinemachineFreeLook")]
  24. [HelpURL(Documentation.BaseURL + "manual/CinemachineFreeLook.html")]
  25. public class CinemachineFreeLook : CinemachineVirtualCameraBase
  26. {
  27. /// <summary>Object for the camera children to look at (the aim target)</summary>
  28. [Tooltip("Object for the camera children to look at (the aim target).")]
  29. [NoSaveDuringPlay]
  30. [VcamTargetProperty]
  31. public Transform m_LookAt = null;
  32. /// <summary>Object for the camera children wants to move with (the body target)</summary>
  33. [Tooltip("Object for the camera children wants to move with (the body target).")]
  34. [NoSaveDuringPlay]
  35. [VcamTargetProperty]
  36. public Transform m_Follow = null;
  37. /// <summary>If enabled, this lens setting will apply to all three child rigs, otherwise the child rig lens settings will be used</summary>
  38. [Tooltip("If enabled, this lens setting will apply to all three child rigs, otherwise the child rig lens settings will be used")]
  39. [FormerlySerializedAs("m_UseCommonLensSetting")]
  40. public bool m_CommonLens = true;
  41. /// <summary>Specifies the lens properties of this Virtual Camera.
  42. /// This generally mirrors the Unity Camera's lens settings, and will be used to drive
  43. /// the Unity camera when the vcam is active</summary>
  44. [FormerlySerializedAs("m_LensAttributes")]
  45. [Tooltip("Specifies the lens properties of this Virtual Camera. This generally mirrors the Unity Camera's lens settings, and will be used to drive the Unity camera when the vcam is active")]
  46. [LensSettingsProperty]
  47. public LensSettings m_Lens = LensSettings.Default;
  48. /// <summary> Collection of parameters that influence how this virtual camera transitions from
  49. /// other virtual cameras </summary>
  50. public TransitionParams m_Transitions;
  51. /// <summary>Legacy support</summary>
  52. [SerializeField] [HideInInspector]
  53. [FormerlySerializedAs("m_BlendHint")]
  54. [FormerlySerializedAs("m_PositionBlending")] private BlendHint m_LegacyBlendHint;
  55. /// <summary>The Vertical axis. Value is 0..1. Chooses how to blend the child rigs</summary>
  56. [Header("Axis Control")]
  57. [Tooltip("The Vertical axis. Value is 0..1. Chooses how to blend the child rigs")]
  58. [AxisStateProperty]
  59. public AxisState m_YAxis = new AxisState(0, 1, false, true, 2f, 0.2f, 0.1f, "Mouse Y", false);
  60. /// <summary>Controls how automatic recentering of the Y axis is accomplished</summary>
  61. [Tooltip("Controls how automatic recentering of the Y axis is accomplished")]
  62. public AxisState.Recentering m_YAxisRecentering = new AxisState.Recentering(false, 1, 2);
  63. /// <summary>The Horizontal axis. Value is -180...180. This is passed on to the rigs' OrbitalTransposer component</summary>
  64. [Tooltip("The Horizontal axis. Value is -180...180. This is passed on to the rigs' OrbitalTransposer component")]
  65. [AxisStateProperty]
  66. public AxisState m_XAxis = new AxisState(-180, 180, true, false, 300f, 0.1f, 0.1f, "Mouse X", true);
  67. /// <summary>The definition of Forward. Camera will follow behind</summary>
  68. [OrbitalTransposerHeadingProperty]
  69. [Tooltip("The definition of Forward. Camera will follow behind.")]
  70. public CinemachineOrbitalTransposer.Heading m_Heading
  71. = new CinemachineOrbitalTransposer.Heading(
  72. CinemachineOrbitalTransposer.Heading.HeadingDefinition.TargetForward, 4, 0);
  73. /// <summary>Controls how automatic recentering of the X axis is accomplished</summary>
  74. [Tooltip("Controls how automatic recentering of the X axis is accomplished")]
  75. public AxisState.Recentering m_RecenterToTargetHeading = new AxisState.Recentering(false, 1, 2);
  76. /// <summary>The coordinate space to use when interpreting the offset from the target</summary>
  77. [Header("Orbits")]
  78. [Tooltip("The coordinate space to use when interpreting the offset from the target. This is also used to set the camera's Up vector, which will be maintained when aiming the camera.")]
  79. public CinemachineOrbitalTransposer.BindingMode m_BindingMode
  80. = CinemachineOrbitalTransposer.BindingMode.SimpleFollowWithWorldUp;
  81. /// <summary></summary>
  82. [Tooltip("Controls how taut is the line that connects the rigs' orbits, which determines final placement on the Y axis")]
  83. [Range(0f, 1f)]
  84. [FormerlySerializedAs("m_SplineTension")]
  85. public float m_SplineCurvature = 0.2f;
  86. /// <summary>Defines the height and radius of the Rig orbit</summary>
  87. [Serializable]
  88. public struct Orbit
  89. {
  90. /// <summary>Height relative to target</summary>
  91. public float m_Height;
  92. /// <summary>Radius of orbit</summary>
  93. public float m_Radius;
  94. /// <summary>Constructor with specific values</summary>
  95. /// <param name="h">Orbit height</param>
  96. /// <param name="r">Orbit radius</param>
  97. public Orbit(float h, float r) { m_Height = h; m_Radius = r; }
  98. }
  99. /// <summary>The radius and height of the three orbiting rigs</summary>
  100. [Tooltip("The radius and height of the three orbiting rigs.")]
  101. public Orbit[] m_Orbits = new Orbit[3]
  102. {
  103. // These are the default orbits
  104. new Orbit(4.5f, 1.75f),
  105. new Orbit(2.5f, 3f),
  106. new Orbit(0.4f, 1.3f)
  107. };
  108. // Legacy support
  109. [SerializeField] [HideInInspector] [FormerlySerializedAs("m_HeadingBias")]
  110. private float m_LegacyHeadingBias = float.MaxValue;
  111. bool mUseLegacyRigDefinitions = false;
  112. /// <summary>Enforce bounds for fields, when changed in inspector.</summary>
  113. protected override void OnValidate()
  114. {
  115. base.OnValidate();
  116. // Upgrade after a legacy deserialize
  117. if (m_LegacyHeadingBias != float.MaxValue)
  118. {
  119. m_Heading.m_Bias= m_LegacyHeadingBias;
  120. m_LegacyHeadingBias = float.MaxValue;
  121. int heading = (int)m_Heading.m_Definition;
  122. if (m_RecenterToTargetHeading.LegacyUpgrade(ref heading, ref m_Heading.m_VelocityFilterStrength))
  123. m_Heading.m_Definition = (CinemachineOrbitalTransposer.Heading.HeadingDefinition)heading;
  124. mUseLegacyRigDefinitions = true;
  125. }
  126. if (m_LegacyBlendHint != BlendHint.None)
  127. {
  128. m_Transitions.m_BlendHint = m_LegacyBlendHint;
  129. m_LegacyBlendHint = BlendHint.None;
  130. }
  131. m_YAxis.Validate();
  132. m_XAxis.Validate();
  133. m_RecenterToTargetHeading.Validate();
  134. m_YAxisRecentering.Validate();
  135. m_Lens.Validate();
  136. InvalidateRigCache();
  137. #if UNITY_EDITOR
  138. for (int i = 0; m_Rigs != null && i < 3 && i < m_Rigs.Length; ++i)
  139. if (m_Rigs[i] != null)
  140. CinemachineVirtualCamera.SetFlagsForHiddenChild(m_Rigs[i].gameObject);
  141. #endif
  142. }
  143. /// <summary>Get a child rig</summary>
  144. /// <param name="i">Rig index. Can be 0, 1, or 2</param>
  145. /// <returns>The rig, or null if index is bad.</returns>
  146. public CinemachineVirtualCamera GetRig(int i)
  147. {
  148. UpdateRigCache();
  149. return (i < 0 || i > 2) ? null : m_Rigs[i];
  150. }
  151. /// <summary>Names of the 3 child rigs</summary>
  152. public static string[] RigNames { get { return new string[] { "TopRig", "MiddleRig", "BottomRig" }; } }
  153. bool mIsDestroyed = false;
  154. /// <summary>Updates the child rig cache</summary>
  155. protected override void OnEnable()
  156. {
  157. mIsDestroyed = false;
  158. base.OnEnable();
  159. InvalidateRigCache();
  160. UpdateInputAxisProvider();
  161. }
  162. /// <summary>
  163. /// API for the inspector. Internal use only
  164. /// </summary>
  165. public void UpdateInputAxisProvider()
  166. {
  167. m_XAxis.SetInputAxisProvider(0, null);
  168. m_YAxis.SetInputAxisProvider(1, null);
  169. var provider = GetInputAxisProvider();
  170. if (provider != null)
  171. {
  172. m_XAxis.SetInputAxisProvider(0, provider);
  173. m_YAxis.SetInputAxisProvider(1, provider);
  174. }
  175. }
  176. /// <summary>Makes sure that the child rigs get destroyed in an undo-firndly manner.
  177. /// Invalidates the rig cache.</summary>
  178. protected override void OnDestroy()
  179. {
  180. // Make the rigs visible instead of destroying - this is to keep Undo happy
  181. if (m_Rigs != null)
  182. foreach (var rig in m_Rigs)
  183. if (rig != null && rig.gameObject != null)
  184. rig.gameObject.hideFlags
  185. &= ~(HideFlags.HideInHierarchy | HideFlags.HideInInspector);
  186. mIsDestroyed = true;
  187. base.OnDestroy();
  188. }
  189. /// <summary>Invalidates the rig cache</summary>
  190. void OnTransformChildrenChanged()
  191. {
  192. InvalidateRigCache();
  193. }
  194. void Reset()
  195. {
  196. #if UNITY_EDITOR
  197. if (RuntimeUtility.IsPrefab(gameObject))
  198. {
  199. Debug.Log("You cannot reset a prefab instance. "
  200. + "First disconnect this instance from the prefab, or enter Prefab Edit mode");
  201. return;
  202. }
  203. #endif
  204. DestroyRigs();
  205. }
  206. /// <summary>Set this to force the next update to ignore deltaTime and reset itself</summary>
  207. public override bool PreviousStateIsValid
  208. {
  209. get { return base.PreviousStateIsValid; }
  210. set
  211. {
  212. if (value == false)
  213. for (int i = 0; m_Rigs != null && i < m_Rigs.Length; ++i)
  214. if (m_Rigs[i] != null)
  215. m_Rigs[i].PreviousStateIsValid = value;
  216. base.PreviousStateIsValid = value;
  217. }
  218. }
  219. /// <summary>The cacmera state, which will be a blend of the child rig states</summary>
  220. override public CameraState State { get { return m_State; } }
  221. /// <summary>Get the current LookAt target. Returns parent's LookAt if parent
  222. /// is non-null and no specific LookAt defined for this camera</summary>
  223. override public Transform LookAt
  224. {
  225. get { return ResolveLookAt(m_LookAt); }
  226. set { m_LookAt = value; }
  227. }
  228. /// <summary>Get the current Follow target. Returns parent's Follow if parent
  229. /// is non-null and no specific Follow defined for this camera</summary>
  230. override public Transform Follow
  231. {
  232. get { return ResolveFollow(m_Follow); }
  233. set { m_Follow = value; }
  234. }
  235. /// <summary>Check whether the vcam a live child of this camera.
  236. /// Returns true if the child is currently contributing actively to the camera state.</summary>
  237. /// <param name="vcam">The Virtual Camera to check</param>
  238. /// <param name="dominantChildOnly">If truw, will only return true if this vcam is the dominat live child</param>
  239. /// <returns>True if the vcam is currently actively influencing the state of this vcam</returns>
  240. public override bool IsLiveChild(ICinemachineCamera vcam, bool dominantChildOnly = false)
  241. {
  242. // Do not update the rig cache here or there will be infinite loop at creation time
  243. if (m_Rigs == null || m_Rigs.Length != 3)
  244. return false;
  245. var y = GetYAxisValue();
  246. if (dominantChildOnly)
  247. {
  248. if (vcam == (ICinemachineCamera)m_Rigs[0])
  249. return y > 0.666f;
  250. if (vcam == (ICinemachineCamera)m_Rigs[2])
  251. return y < 0.333;
  252. if (vcam == (ICinemachineCamera)m_Rigs[1])
  253. return y >= 0.333f && y <= 0.666f;
  254. return false;
  255. }
  256. if (vcam == (ICinemachineCamera)m_Rigs[1])
  257. return true;
  258. if (y < 0.5f)
  259. return vcam == (ICinemachineCamera)m_Rigs[2];
  260. return vcam == (ICinemachineCamera)m_Rigs[0];
  261. }
  262. /// <summary>This is called to notify the vcam that a target got warped,
  263. /// so that the vcam can update its internal state to make the camera
  264. /// also warp seamlessy.</summary>
  265. /// <param name="target">The object that was warped</param>
  266. /// <param name="positionDelta">The amount the target's position changed</param>
  267. public override void OnTargetObjectWarped(Transform target, Vector3 positionDelta)
  268. {
  269. UpdateRigCache();
  270. if (m_Rigs != null)
  271. foreach (var vcam in m_Rigs)
  272. vcam.OnTargetObjectWarped(target, positionDelta);
  273. base.OnTargetObjectWarped(target, positionDelta);
  274. }
  275. /// <summary>
  276. /// Force the virtual camera to assume a given position and orientation.
  277. /// Procedural placement then takes over
  278. /// </summary>
  279. /// <param name="pos">Worldspace pposition to take</param>
  280. /// <param name="rot">Worldspace orientation to take</param>
  281. public override void ForceCameraPosition(Vector3 pos, Quaternion rot)
  282. {
  283. var up = State.ReferenceUp;
  284. m_YAxis.Value = GetYAxisClosestValue(pos, up);
  285. PreviousStateIsValid = true;
  286. transform.position = pos;
  287. transform.rotation = rot;
  288. m_State.RawPosition = pos;
  289. m_State.RawOrientation = rot;
  290. UpdateRigCache();
  291. if (m_BindingMode != CinemachineTransposer.BindingMode.SimpleFollowWithWorldUp)
  292. m_XAxis.Value = mOrbitals[1].GetAxisClosestValue(pos, up);
  293. PushSettingsToRigs();
  294. for (int i = 0; i < 3; ++i)
  295. m_Rigs[i].ForceCameraPosition(pos, rot);
  296. InternalUpdateCameraState(up, -1);
  297. base.ForceCameraPosition(pos, rot);
  298. }
  299. /// <summary>Internal use only. Called by CinemachineCore at designated update time
  300. /// so the vcam can position itself and track its targets. All 3 child rigs are updated,
  301. /// and a blend calculated, depending on the value of the Y axis.</summary>
  302. /// <param name="worldUp">Default world Up, set by the CinemachineBrain</param>
  303. /// <param name="deltaTime">Delta time for time-based effects (ignore if less than 0)</param>
  304. override public void InternalUpdateCameraState(Vector3 worldUp, float deltaTime)
  305. {
  306. UpdateRigCache();
  307. // Update the current state by invoking the component pipeline
  308. m_State = CalculateNewState(worldUp, deltaTime);
  309. ApplyPositionBlendMethod(ref m_State, m_Transitions.m_BlendHint);
  310. // Push the raw position back to the game object's transform, so it
  311. // moves along with the camera. Leave the orientation alone, because it
  312. // screws up camera dragging when there is a LookAt behaviour.
  313. if (Follow != null)
  314. {
  315. Vector3 delta = State.RawPosition - transform.position;
  316. transform.position = State.RawPosition;
  317. m_Rigs[0].transform.position -= delta;
  318. m_Rigs[1].transform.position -= delta;
  319. m_Rigs[2].transform.position -= delta;
  320. }
  321. InvokePostPipelineStageCallback(this, CinemachineCore.Stage.Finalize, ref m_State, deltaTime);
  322. PreviousStateIsValid = true;
  323. // Set up for next frame
  324. bool activeCam = PreviousStateIsValid && CinemachineCore.Instance.IsLive(this);
  325. if (activeCam && deltaTime >= 0)
  326. {
  327. if (m_YAxis.Update(deltaTime))
  328. m_YAxisRecentering.CancelRecentering();
  329. }
  330. PushSettingsToRigs();
  331. if (m_BindingMode == CinemachineTransposer.BindingMode.SimpleFollowWithWorldUp)
  332. m_XAxis.Value = 0;
  333. }
  334. /// <summary>If we are transitioning from another FreeLook, grab the axis values from it.</summary>
  335. /// <param name="fromCam">The camera being deactivated. May be null.</param>
  336. /// <param name="worldUp">Default world Up, set by the CinemachineBrain</param>
  337. /// <param name="deltaTime">Delta time for time-based effects (ignore if less than or equal to 0)</param>
  338. public override void OnTransitionFromCamera(
  339. ICinemachineCamera fromCam, Vector3 worldUp, float deltaTime)
  340. {
  341. base.OnTransitionFromCamera(fromCam, worldUp, deltaTime);
  342. InvokeOnTransitionInExtensions(fromCam, worldUp, deltaTime);
  343. bool forceUpdate = false;
  344. // m_RecenterToTargetHeading.DoRecentering(ref m_XAxis, -1, 0);
  345. // m_YAxis.m_Recentering.DoRecentering(ref m_YAxis, -1, 0.5f);
  346. // m_RecenterToTargetHeading.CancelRecentering();
  347. // m_YAxis.m_Recentering.CancelRecentering();
  348. if (fromCam != null && m_Transitions.m_InheritPosition)
  349. {
  350. var cameraPos = fromCam.State.RawPosition;
  351. // Special handling for FreeLook: get an undamped outgoing position
  352. if (fromCam is CinemachineFreeLook)
  353. {
  354. var flFrom = (fromCam as CinemachineFreeLook);
  355. var orbital = flFrom.mOrbitals != null ? flFrom.mOrbitals[1] : null;
  356. if (orbital != null)
  357. cameraPos = orbital.GetTargetCameraPosition(worldUp);
  358. }
  359. UpdateRigCache();
  360. if (m_BindingMode != CinemachineTransposer.BindingMode.SimpleFollowWithWorldUp)
  361. m_XAxis.Value = mOrbitals[1].GetAxisClosestValue(cameraPos, worldUp);
  362. m_YAxis.Value = GetYAxisClosestValue(cameraPos, worldUp);
  363. transform.position = cameraPos;
  364. transform.rotation = fromCam.State.RawOrientation;
  365. m_State = PullStateFromVirtualCamera(worldUp, ref m_Lens);
  366. PreviousStateIsValid = false;
  367. PushSettingsToRigs();
  368. forceUpdate = true;
  369. }
  370. if (forceUpdate)
  371. {
  372. for (int i = 0; i < 3; ++i)
  373. m_Rigs[i].InternalUpdateCameraState(worldUp, deltaTime);
  374. InternalUpdateCameraState(worldUp, deltaTime);
  375. }
  376. else
  377. UpdateCameraState(worldUp, deltaTime);
  378. if (m_Transitions.m_OnCameraLive != null)
  379. m_Transitions.m_OnCameraLive.Invoke(this, fromCam);
  380. }
  381. float GetYAxisClosestValue(Vector3 cameraPos, Vector3 up)
  382. {
  383. if (Follow != null)
  384. {
  385. // Rotate the camera pos to the back
  386. Quaternion q = Quaternion.FromToRotation(up, Vector3.up);
  387. Vector3 dir = q * (cameraPos - Follow.position);
  388. Vector3 flatDir = dir; flatDir.y = 0;
  389. if (!flatDir.AlmostZero())
  390. {
  391. float angle = Vector3.SignedAngle(flatDir, Vector3.back, Vector3.up);
  392. dir = Quaternion.AngleAxis(angle, Vector3.up) * dir;
  393. }
  394. dir.x = 0;
  395. // Sample the spline in a few places, find the 2 closest, and lerp
  396. int i0 = 0, i1 = 0;
  397. float a0 = 0, a1 = 0;
  398. const int NumSamples = 13;
  399. float step = 1f / (NumSamples-1);
  400. for (int i = 0; i < NumSamples; ++i)
  401. {
  402. float a = Vector3.SignedAngle(
  403. dir, GetLocalPositionForCameraFromInput(i * step), Vector3.right);
  404. if (i == 0)
  405. a0 = a1 = a;
  406. else
  407. {
  408. if (Mathf.Abs(a) < Mathf.Abs(a0))
  409. {
  410. a1 = a0;
  411. i1 = i0;
  412. a0 = a;
  413. i0 = i;
  414. }
  415. else if (Mathf.Abs(a) < Mathf.Abs(a1))
  416. {
  417. a1 = a;
  418. i1 = i;
  419. }
  420. }
  421. }
  422. if (Mathf.Sign(a0) == Mathf.Sign(a1))
  423. return i0 * step;
  424. float t = Mathf.Abs(a0) / (Mathf.Abs(a0) + Mathf.Abs(a1));
  425. return Mathf.Lerp(i0 * step, i1 * step, t);
  426. }
  427. return m_YAxis.Value; // stay conservative
  428. }
  429. CameraState m_State = CameraState.Default; // Current state this frame
  430. /// Serialized in order to support copy/paste
  431. [SerializeField][HideInInspector][NoSaveDuringPlay]
  432. private CinemachineVirtualCamera[] m_Rigs = new CinemachineVirtualCamera[3];
  433. void InvalidateRigCache() { mOrbitals = null; }
  434. CinemachineOrbitalTransposer[] mOrbitals = null;
  435. CinemachineBlend mBlendA;
  436. CinemachineBlend mBlendB;
  437. /// <summary>
  438. /// Override component pipeline creation.
  439. /// This needs to be done by the editor to support Undo.
  440. /// The override must do exactly the same thing as the CreatePipeline method in this class.
  441. /// </summary>
  442. public static CreateRigDelegate CreateRigOverride;
  443. /// <summary>
  444. /// Override component pipeline creation.
  445. /// This needs to be done by the editor to support Undo.
  446. /// The override must do exactly the same thing as the CreatePipeline method in this class.
  447. /// </summary>
  448. public delegate CinemachineVirtualCamera CreateRigDelegate(
  449. CinemachineFreeLook vcam, string name, CinemachineVirtualCamera copyFrom);
  450. /// <summary>
  451. /// Override component pipeline destruction.
  452. /// This needs to be done by the editor to support Undo.
  453. /// </summary>
  454. public static DestroyRigDelegate DestroyRigOverride;
  455. /// <summary>
  456. /// Override component pipeline destruction.
  457. /// This needs to be done by the editor to support Undo.
  458. /// </summary>
  459. public delegate void DestroyRigDelegate(GameObject rig);
  460. private void DestroyRigs()
  461. {
  462. CinemachineVirtualCamera[] oldRigs = new CinemachineVirtualCamera[RigNames.Length];
  463. for (int i = 0; i < RigNames.Length; ++i)
  464. {
  465. foreach (Transform child in transform)
  466. if (child.gameObject.name == RigNames[i])
  467. oldRigs[i] = child.GetComponent<CinemachineVirtualCamera>();
  468. }
  469. for (int i = 0; i < oldRigs.Length; ++i)
  470. {
  471. if (oldRigs[i] != null)
  472. {
  473. if (DestroyRigOverride != null)
  474. DestroyRigOverride(oldRigs[i].gameObject);
  475. else
  476. Destroy(oldRigs[i].gameObject);
  477. }
  478. }
  479. m_Rigs = null;
  480. mOrbitals = null;
  481. }
  482. private CinemachineVirtualCamera[] CreateRigs(CinemachineVirtualCamera[] copyFrom)
  483. {
  484. // Invalidate the cache
  485. mOrbitals = null;
  486. float[] softCenterDefaultsV = new float[] { 0.5f, 0.55f, 0.6f };
  487. CinemachineVirtualCamera[] newRigs = new CinemachineVirtualCamera[3];
  488. for (int i = 0; i < RigNames.Length; ++i)
  489. {
  490. CinemachineVirtualCamera src = null;
  491. if (copyFrom != null && copyFrom.Length > i)
  492. src = copyFrom[i];
  493. if (CreateRigOverride != null)
  494. newRigs[i] = CreateRigOverride(this, RigNames[i], src);
  495. else
  496. {
  497. // Create a new rig with default components
  498. // Note: copyFrom only supported in Editor, not build
  499. GameObject go = new GameObject(RigNames[i]);
  500. go.transform.parent = transform;
  501. newRigs[i] = go.AddComponent<CinemachineVirtualCamera>();
  502. go = newRigs[i].GetComponentOwner().gameObject;
  503. go.AddComponent<CinemachineOrbitalTransposer>();
  504. go.AddComponent<CinemachineComposer>();
  505. }
  506. // Set up the defaults
  507. newRigs[i].InvalidateComponentPipeline();
  508. CinemachineOrbitalTransposer orbital = newRigs[i].GetCinemachineComponent<CinemachineOrbitalTransposer>();
  509. if (orbital == null)
  510. orbital = newRigs[i].AddCinemachineComponent<CinemachineOrbitalTransposer>(); // should not happen
  511. if (src == null)
  512. {
  513. // Only set defaults if not copying
  514. orbital.m_YawDamping = 0;
  515. CinemachineComposer composer = newRigs[i].GetCinemachineComponent<CinemachineComposer>();
  516. if (composer != null)
  517. {
  518. composer.m_HorizontalDamping = composer.m_VerticalDamping = 0;
  519. composer.m_ScreenX = 0.5f;
  520. composer.m_ScreenY = softCenterDefaultsV[i];
  521. composer.m_DeadZoneWidth = composer.m_DeadZoneHeight = 0f;
  522. composer.m_SoftZoneWidth = composer.m_SoftZoneHeight = 0.8f;
  523. composer.m_BiasX = composer.m_BiasY = 0;
  524. }
  525. }
  526. }
  527. return newRigs;
  528. }
  529. private void UpdateRigCache()
  530. {
  531. if (mIsDestroyed)
  532. return;
  533. bool isPrefab = RuntimeUtility.IsPrefab(gameObject);
  534. #if UNITY_EDITOR
  535. // Special condition: Did we just get copy/pasted?
  536. if (m_Rigs != null && m_Rigs.Length == 3
  537. && m_Rigs[0] != null && m_Rigs[0].transform.parent != transform)
  538. {
  539. if (!isPrefab) // can't paste to a prefab
  540. {
  541. var copyFrom = m_Rigs;
  542. DestroyRigs();
  543. m_Rigs = CreateRigs(copyFrom);
  544. }
  545. }
  546. #endif
  547. // Early out if we're up to date
  548. if (mOrbitals != null && mOrbitals.Length == 3)
  549. return;
  550. // Locate existing rigs, and recreate them if any are missing
  551. if (LocateExistingRigs(RigNames, false) != 3 && !isPrefab)
  552. {
  553. DestroyRigs();
  554. m_Rigs = CreateRigs(null);
  555. LocateExistingRigs(RigNames, true);
  556. }
  557. #if UNITY_EDITOR
  558. foreach (var rig in m_Rigs)
  559. {
  560. // Configure the UI
  561. if (rig == null)
  562. continue;
  563. rig.m_ExcludedPropertiesInInspector = m_CommonLens
  564. ? new string[] { "m_Script", "Header", "Extensions", "m_Priority", "m_Transitions", "m_Follow", "m_StandbyUpdate", "m_Lens" }
  565. : new string[] { "m_Script", "Header", "Extensions", "m_Priority", "m_Transitions", "m_Follow", "m_StandbyUpdate" };
  566. rig.m_LockStageInInspector = new CinemachineCore.Stage[] { CinemachineCore.Stage.Body };
  567. }
  568. #endif
  569. // Create the blend objects
  570. mBlendA = new CinemachineBlend(m_Rigs[1], m_Rigs[0], AnimationCurve.Linear(0, 0, 1, 1), 1, 0);
  571. mBlendB = new CinemachineBlend(m_Rigs[2], m_Rigs[1], AnimationCurve.Linear(0, 0, 1, 1), 1, 0);
  572. }
  573. private int LocateExistingRigs(string[] rigNames, bool forceOrbital)
  574. {
  575. CachedXAxisHeading = 0;
  576. mOrbitals = new CinemachineOrbitalTransposer[rigNames.Length];
  577. m_Rigs = new CinemachineVirtualCamera[rigNames.Length];
  578. int rigsFound = 0;
  579. foreach (Transform child in transform)
  580. {
  581. CinemachineVirtualCamera vcam = child.GetComponent<CinemachineVirtualCamera>();
  582. if (vcam != null)
  583. {
  584. GameObject go = child.gameObject;
  585. for (int i = 0; i < rigNames.Length; ++i)
  586. {
  587. if (mOrbitals[i] == null && go.name == rigNames[i])
  588. {
  589. // Must have an orbital transposer or it's no good
  590. mOrbitals[i] = vcam.GetCinemachineComponent<CinemachineOrbitalTransposer>();
  591. if (mOrbitals[i] == null && forceOrbital)
  592. mOrbitals[i] = vcam.AddCinemachineComponent<CinemachineOrbitalTransposer>();
  593. if (mOrbitals[i] != null)
  594. {
  595. mOrbitals[i].m_HeadingIsSlave = true;
  596. mOrbitals[i].m_XAxis.m_InputAxisName = string.Empty;
  597. mOrbitals[i].HeadingUpdater = UpdateXAxisHeading;
  598. mOrbitals[i].m_RecenterToTargetHeading.m_enabled = false;
  599. m_Rigs[i] = vcam;
  600. m_Rigs[i].m_StandbyUpdate = m_StandbyUpdate;
  601. ++rigsFound;
  602. }
  603. }
  604. }
  605. }
  606. }
  607. return rigsFound;
  608. }
  609. float CachedXAxisHeading { get; set; }
  610. float UpdateXAxisHeading(CinemachineOrbitalTransposer orbital, float deltaTime, Vector3 up)
  611. {
  612. if (this == null)
  613. return 0; // deleted
  614. if (mOrbitals != null && mOrbitals[1] == orbital)
  615. {
  616. var oldValue = m_XAxis.Value;
  617. CachedXAxisHeading = orbital.UpdateHeading(
  618. PreviousStateIsValid ? deltaTime : -1, up,
  619. ref m_XAxis, ref m_RecenterToTargetHeading,
  620. CinemachineCore.Instance.IsLive(this));
  621. // Allow externally-driven values to work in this mode
  622. if (m_BindingMode == CinemachineTransposer.BindingMode.SimpleFollowWithWorldUp)
  623. m_XAxis.Value = oldValue;
  624. }
  625. return CachedXAxisHeading;
  626. }
  627. void PushSettingsToRigs()
  628. {
  629. UpdateRigCache();
  630. for (int i = 0; i < m_Rigs.Length; ++i)
  631. {
  632. if (m_Rigs[i] == null)
  633. continue;
  634. if (m_CommonLens)
  635. m_Rigs[i].m_Lens = m_Lens;
  636. // If we just deserialized from a legacy version,
  637. // pull the orbits and targets from the rigs
  638. if (mUseLegacyRigDefinitions)
  639. {
  640. mUseLegacyRigDefinitions = false;
  641. m_Orbits[i].m_Height = mOrbitals[i].m_FollowOffset.y;
  642. m_Orbits[i].m_Radius = -mOrbitals[i].m_FollowOffset.z;
  643. if (m_Rigs[i].Follow != null)
  644. Follow = m_Rigs[i].Follow;
  645. }
  646. m_Rigs[i].Follow = null;
  647. m_Rigs[i].m_StandbyUpdate = m_StandbyUpdate;
  648. m_Rigs[i].FollowTargetAttachment = FollowTargetAttachment;
  649. m_Rigs[i].LookAtTargetAttachment = LookAtTargetAttachment;
  650. if (!PreviousStateIsValid)
  651. {
  652. m_Rigs[i].PreviousStateIsValid = false;
  653. m_Rigs[i].transform.position = transform.position;
  654. m_Rigs[i].transform.rotation = transform.rotation;
  655. }
  656. #if UNITY_EDITOR
  657. // Hide the rigs from prying eyes
  658. CinemachineVirtualCamera.SetFlagsForHiddenChild(m_Rigs[i].gameObject);
  659. #endif
  660. mOrbitals[i].m_FollowOffset = GetLocalPositionForCameraFromInput(GetYAxisValue());
  661. mOrbitals[i].m_BindingMode = m_BindingMode;
  662. mOrbitals[i].m_Heading = m_Heading;
  663. mOrbitals[i].m_XAxis.Value = m_XAxis.Value;
  664. // Hack to get SimpleFollow with heterogeneous dampings to work
  665. if (m_BindingMode == CinemachineTransposer.BindingMode.SimpleFollowWithWorldUp)
  666. m_Rigs[i].SetStateRawPosition(State.RawPosition);
  667. }
  668. }
  669. private float GetYAxisValue()
  670. {
  671. float range = m_YAxis.m_MaxValue - m_YAxis.m_MinValue;
  672. return (range > UnityVectorExtensions.Epsilon) ? m_YAxis.Value / range : 0.5f;
  673. }
  674. private CameraState CalculateNewState(Vector3 worldUp, float deltaTime)
  675. {
  676. CameraState state = PullStateFromVirtualCamera(worldUp, ref m_Lens);
  677. m_YAxisRecentering.DoRecentering(ref m_YAxis, deltaTime, 0.5f);
  678. // Blend from the appropriate rigs
  679. float t = GetYAxisValue();
  680. if (t > 0.5f)
  681. {
  682. if (mBlendA != null)
  683. {
  684. mBlendA.TimeInBlend = (t - 0.5f) * 2f;
  685. mBlendA.UpdateCameraState(worldUp, deltaTime);
  686. state = mBlendA.State;
  687. }
  688. }
  689. else
  690. {
  691. if (mBlendB != null)
  692. {
  693. mBlendB.TimeInBlend = t * 2f;
  694. mBlendB.UpdateCameraState(worldUp, deltaTime);
  695. state = mBlendB.State;
  696. }
  697. }
  698. return state;
  699. }
  700. /// <summary>
  701. /// Returns the local position of the camera along the spline used to connect the
  702. /// three camera rigs. Does not take into account the current heading of the
  703. /// camera (or its target)
  704. /// </summary>
  705. /// <param name="t">The t-value for the camera on its spline. Internally clamped to
  706. /// the value [0,1]</param>
  707. /// <returns>The local offset (back + up) of the camera WRT its target based on the
  708. /// supplied t-value</returns>
  709. public Vector3 GetLocalPositionForCameraFromInput(float t)
  710. {
  711. if (mOrbitals == null)
  712. return Vector3.zero;
  713. UpdateCachedSpline();
  714. int n = 1;
  715. if (t > 0.5f)
  716. {
  717. t -= 0.5f;
  718. n = 2;
  719. }
  720. return SplineHelpers.Bezier3(
  721. t * 2f, m_CachedKnots[n], m_CachedCtrl1[n], m_CachedCtrl2[n], m_CachedKnots[n+1]);
  722. }
  723. Orbit[] m_CachedOrbits;
  724. float m_CachedTension;
  725. Vector4[] m_CachedKnots;
  726. Vector4[] m_CachedCtrl1;
  727. Vector4[] m_CachedCtrl2;
  728. void UpdateCachedSpline()
  729. {
  730. bool cacheIsValid = (m_CachedOrbits != null && m_CachedOrbits.Length == 3
  731. && m_CachedTension == m_SplineCurvature);
  732. for (int i = 0; i < 3 && cacheIsValid; ++i)
  733. cacheIsValid = (m_CachedOrbits[i].m_Height == m_Orbits[i].m_Height
  734. && m_CachedOrbits[i].m_Radius == m_Orbits[i].m_Radius);
  735. if (!cacheIsValid)
  736. {
  737. float t = m_SplineCurvature;
  738. m_CachedKnots = new Vector4[5];
  739. m_CachedCtrl1 = new Vector4[5];
  740. m_CachedCtrl2 = new Vector4[5];
  741. m_CachedKnots[1] = new Vector4(0, m_Orbits[2].m_Height, -m_Orbits[2].m_Radius, 0);
  742. m_CachedKnots[2] = new Vector4(0, m_Orbits[1].m_Height, -m_Orbits[1].m_Radius, 0);
  743. m_CachedKnots[3] = new Vector4(0, m_Orbits[0].m_Height, -m_Orbits[0].m_Radius, 0);
  744. m_CachedKnots[0] = Vector4.Lerp(m_CachedKnots[1], Vector4.zero, t);
  745. m_CachedKnots[4] = Vector4.Lerp(m_CachedKnots[3], Vector4.zero, t);
  746. SplineHelpers.ComputeSmoothControlPoints(
  747. ref m_CachedKnots, ref m_CachedCtrl1, ref m_CachedCtrl2);
  748. m_CachedOrbits = new Orbit[3];
  749. for (int i = 0; i < 3; ++i)
  750. m_CachedOrbits[i] = m_Orbits[i];
  751. m_CachedTension = m_SplineCurvature;
  752. }
  753. }
  754. }
  755. }