| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444 |
- #if CINEMACHINE_EXPERIMENTAL_VCAM
- using UnityEngine;
- using System;
- namespace Cinemachine
- {
- /// <summary>
- ///
- /// NOTE: THIS CLASS IS EXPERIMENTAL, AND NOT FOR PUBLIC USE
- ///
- /// Lighter-weight version of the CinemachineVirtualCamera.
- ///
- /// </summary>
- [DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)]
- [DisallowMultipleComponent]
- #if UNITY_2018_3_OR_NEWER
- [ExecuteAlways]
- #else
- [ExecuteInEditMode]
- #endif
- [AddComponentMenu("Cinemachine/CinemachineNewVirtualCamera")]
- public class CinemachineNewVirtualCamera : CinemachineVirtualCameraBase
- {
- /// <summary>Object for the camera children to look at (the aim target)</summary>
- [Tooltip("Object for the camera children to look at (the aim target).")]
- [NoSaveDuringPlay]
- [VcamTargetProperty]
- public Transform m_LookAt = null;
- /// <summary>Object for the camera children wants to move with (the body target)</summary>
- [Tooltip("Object for the camera children wants to move with (the body target).")]
- [NoSaveDuringPlay]
- [VcamTargetProperty]
- public Transform m_Follow = null;
- /// <summary>Specifies the LensSettings of this Virtual Camera.
- /// These settings will be transferred to the Unity camera when the vcam is live.</summary>
- [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.")]
- [LensSettingsProperty]
- public LensSettings m_Lens = LensSettings.Default;
- /// <summary> Collection of parameters that influence how this virtual camera transitions from
- /// other virtual cameras </summary>
- public TransitionParams m_Transitions;
- /// <summary>API for the editor, to make the dragging of position handles behave better.</summary>
- public bool UserIsDragging { get; set; }
- /// <summary>Updates the child rig cache</summary>
- protected override void OnEnable()
- {
- base.OnEnable();
- InvalidateComponentCache();
- }
- void Reset()
- {
- DestroyComponents();
- }
- /// <summary>Validates the settings avter inspector edit</summary>
- protected override void OnValidate()
- {
- base.OnValidate();
- m_Lens.Validate();
- }
- /// <summary>The camera state, which will be a blend of the child rig states</summary>
- override public CameraState State { get { return m_State; } }
- /// <summary>The camera state, which will be a blend of the child rig states</summary>
- protected CameraState m_State = CameraState.Default;
- /// <summary>Get the current LookAt target. Returns parent's LookAt if parent
- /// is non-null and no specific LookAt defined for this camera</summary>
- override public Transform LookAt
- {
- get { return ResolveLookAt(m_LookAt); }
- set { m_LookAt = value; }
- }
- /// <summary>Get the current Follow target. Returns parent's Follow if parent
- /// is non-null and no specific Follow defined for this camera</summary>
- override public Transform Follow
- {
- get { return ResolveFollow(m_Follow); }
- set { m_Follow = value; }
- }
- /// <summary>This is called to notify the vcam that a target got warped,
- /// so that the vcam can update its internal state to make the camera
- /// also warp seamlessy.</summary>
- /// <param name="target">The object that was warped</param>
- /// <param name="positionDelta">The amount the target's position changed</param>
- public override void OnTargetObjectWarped(Transform target, Vector3 positionDelta)
- {
- if (target == Follow)
- {
- transform.position += positionDelta;
- m_State.RawPosition += positionDelta;
- }
- UpdateComponentCache();
- for (int i = 0; i < m_Components.Length; ++i)
- {
- if (m_Components[i] != null)
- m_Components[i].OnTargetObjectWarped(target, positionDelta);
- }
- base.OnTargetObjectWarped(target, positionDelta);
- }
- /// <summary>
- /// Force the virtual camera to assume a given position and orientation
- /// </summary>
- /// <param name="pos">Worldspace pposition to take</param>
- /// <param name="rot">Worldspace orientation to take</param>
- public override void ForceCameraPosition(Vector3 pos, Quaternion rot)
- {
- PreviousStateIsValid = false;
- transform.position = pos;
- transform.rotation = rot;
- m_State.RawPosition = pos;
- m_State.RawOrientation = rot;
- UpdateComponentCache();
- for (int i = 0; i < m_Components.Length; ++i)
- if (m_Components[i] != null)
- m_Components[i].ForceCameraPosition(pos, rot);
- base.ForceCameraPosition(pos, rot);
- }
-
- /// <summary>
- /// Query components and extensions for the maximum damping time.
- /// </summary>
- /// <returns>Highest damping setting in this vcam</returns>
- public override float GetMaxDampTime()
- {
- float maxDamp = base.GetMaxDampTime();
- UpdateComponentCache();
- for (int i = 0; i < m_Components.Length; ++i)
- if (m_Components[i] != null)
- maxDamp = Mathf.Max(maxDamp, m_Components[i].GetMaxDampTime());
- return maxDamp;
- }
- /// <summary>If we are transitioning from another FreeLook, grab the axis values from it.</summary>
- /// <param name="fromCam">The camera being deactivated. May be null.</param>
- /// <param name="worldUp">Default world Up, set by the CinemachineBrain</param>
- /// <param name="deltaTime">Delta time for time-based effects (ignore if less than or equal to 0)</param>
- public override void OnTransitionFromCamera(
- ICinemachineCamera fromCam, Vector3 worldUp, float deltaTime)
- {
- base.OnTransitionFromCamera(fromCam, worldUp, deltaTime);
- InvokeOnTransitionInExtensions(fromCam, worldUp, deltaTime);
- bool forceUpdate = false;
- if (m_Transitions.m_InheritPosition && fromCam != null)
- {
- transform.position = fromCam.State.RawPosition;
- //transform.rotation = fromCam.State.RawOrientation;
- PreviousStateIsValid = false;
- forceUpdate = true;
- }
- UpdateComponentCache();
- for (int i = 0; i < m_Components.Length; ++i)
- {
- if (m_Components[i] != null
- && m_Components[i].OnTransitionFromCamera(
- fromCam, worldUp, deltaTime, ref m_Transitions))
- forceUpdate = true;
- }
- if (forceUpdate)
- {
- InternalUpdateCameraState(worldUp, deltaTime);
- InternalUpdateCameraState(worldUp, deltaTime);
- }
- else
- UpdateCameraState(worldUp, deltaTime);
- if (m_Transitions.m_OnCameraLive != null)
- m_Transitions.m_OnCameraLive.Invoke(this, fromCam);
- }
- /// <summary>Internal use only. Called by CinemachineCore at designated update time
- /// so the vcam can position itself and track its targets. All 3 child rigs are updated,
- /// and a blend calculated, depending on the value of the Y axis.</summary>
- /// <param name="worldUp">Default world Up, set by the CinemachineBrain</param>
- /// <param name="deltaTime">Delta time for time-based effects (ignore if less than 0)</param>
- override public void InternalUpdateCameraState(Vector3 worldUp, float deltaTime)
- {
- FollowTargetAttachment = 1;
- LookAtTargetAttachment = 1;
- // Initialize the camera state, in case the game object got moved in the editor
- m_State = PullStateFromVirtualCamera(worldUp, ref m_Lens);
- // Do our stuff
- SetReferenceLookAtTargetInState(ref m_State);
- InvokeComponentPipeline(ref m_State, worldUp, deltaTime);
- ApplyPositionBlendMethod(ref m_State, m_Transitions.m_BlendHint);
- // Push the raw position back to the game object's transform, so it
- // moves along with the camera.
- if (!UserIsDragging)
- {
- if (Follow != null)
- transform.position = State.RawPosition;
- if (LookAt != null)
- transform.rotation = State.RawOrientation;
- }
- // Signal that it's all done
- InvokePostPipelineStageCallback(this, CinemachineCore.Stage.Finalize, ref m_State, deltaTime);
- PreviousStateIsValid = true;
- }
- private Transform mCachedLookAtTarget;
- private CinemachineVirtualCameraBase mCachedLookAtTargetVcam;
- /// <summary>Set the state's refeenceLookAt target to our lookAt, with some smarts
- /// in case our LookAt points to a vcam</summary>
- protected void SetReferenceLookAtTargetInState(ref CameraState state)
- {
- Transform lookAtTarget = LookAt;
- if (lookAtTarget != mCachedLookAtTarget)
- {
- mCachedLookAtTarget = lookAtTarget;
- mCachedLookAtTargetVcam = null;
- if (lookAtTarget != null)
- mCachedLookAtTargetVcam = lookAtTarget.GetComponent<CinemachineVirtualCameraBase>();
- }
- if (lookAtTarget != null)
- {
- if (mCachedLookAtTargetVcam != null)
- state.ReferenceLookAt = mCachedLookAtTargetVcam.State.FinalPosition;
- else
- state.ReferenceLookAt = TargetPositionCache.GetTargetPosition(lookAtTarget);
- }
- }
- protected CameraState InvokeComponentPipeline(
- ref CameraState state, Vector3 worldUp, float deltaTime)
- {
- UpdateComponentCache();
- // Extensions first
- InvokePrePipelineMutateCameraStateCallback(this, ref state, deltaTime);
- // Apply the component pipeline
- for (CinemachineCore.Stage stage = CinemachineCore.Stage.Body;
- stage <= CinemachineCore.Stage.Finalize; ++stage)
- {
- var c = m_Components[(int)stage];
- if (c != null)
- c.PrePipelineMutateCameraState(ref state, deltaTime);
- }
- CinemachineComponentBase postAimBody = null;
- for (CinemachineCore.Stage stage = CinemachineCore.Stage.Body;
- stage <= CinemachineCore.Stage.Finalize; ++stage)
- {
- var c = m_Components[(int)stage];
- if (c != null)
- {
- if (stage == CinemachineCore.Stage.Body && c.BodyAppliesAfterAim)
- {
- postAimBody = c;
- continue; // do the body stage of the pipeline after Aim
- }
- c.MutateCameraState(ref state, deltaTime);
- }
- InvokePostPipelineStageCallback(this, stage, ref state, deltaTime);
- if (stage == CinemachineCore.Stage.Aim)
- {
- if (c == null)
- state.BlendHint |= CameraState.BlendHintValue.IgnoreLookAtTarget; // no aim
- // If we have saved a Body for after Aim, do it now
- if (postAimBody != null)
- {
- postAimBody.MutateCameraState(ref state, deltaTime);
- InvokePostPipelineStageCallback(this, CinemachineCore.Stage.Body, ref state, deltaTime);
- }
- }
- }
- return state;
- }
- // Component Cache - serialized only for copy/paste
- [SerializeField, HideInInspector, NoSaveDuringPlay]
- CinemachineComponentBase[] m_Components;
- /// For inspector
- internal CinemachineComponentBase[] ComponentCache
- {
- get
- {
- UpdateComponentCache();
- return m_Components;
- }
- }
- /// <summary>Call this when CinemachineCompponentBase compponents are added
- /// or removed. If you don't call this, you may get null reference errors.</summary>
- public void InvalidateComponentCache()
- {
- m_Components = null;
- }
- /// <summary>Bring the component cache up to date if needed</summary>
- protected void UpdateComponentCache()
- {
- #if UNITY_EDITOR
- // Special case: if we have serialized in with some other game object's
- // components, then we have just been pasted so we should clone them
- for (int i = 0; m_Components != null && i < m_Components.Length; ++i)
- {
- if (m_Components[i] != null && m_Components[i].gameObject != gameObject)
- {
- var copyFrom = m_Components;
- DestroyComponents();
- CopyComponents(copyFrom);
- break;
- }
- }
- #endif
- if (m_Components != null && m_Components.Length == (int)CinemachineCore.Stage.Finalize + 1)
- return; // up to date
- m_Components = new CinemachineComponentBase[(int)CinemachineCore.Stage.Finalize + 1];
- var existing = GetComponents<CinemachineComponentBase>();
- for (int i = 0; existing != null && i < existing.Length; ++i)
- m_Components[(int)existing[i].Stage] = existing[i];
- for (int i = 0; i < m_Components.Length; ++i)
- {
- if (m_Components[i] != null)
- {
- if (CinemachineCore.sShowHiddenObjects)
- m_Components[i].hideFlags &= ~HideFlags.HideInInspector;
- else
- m_Components[i].hideFlags |= HideFlags.HideInInspector;
- }
- }
- OnComponentCacheUpdated();
- }
- /// <summary>Notification that the component cache has just been update,
- /// in case a subclass needs to do something extra</summary>
- protected virtual void OnComponentCacheUpdated() {}
- /// <summary>Destroy all the CinmachineComponentBase components</summary>
- protected void DestroyComponents()
- {
- var existing = GetComponents<CinemachineComponentBase>();
- for (int i = 0; i < existing.Length; ++i)
- {
- #if UNITY_EDITOR
- UnityEditor.Undo.DestroyObjectImmediate(existing[i]);
- #else
- UnityEngine.Object.Destroy(existing[i]);
- #endif
- }
- InvalidateComponentCache();
- }
- #if UNITY_EDITOR
- // This gets called when user pastes component values
- void CopyComponents(CinemachineComponentBase[] copyFrom)
- {
- foreach (CinemachineComponentBase c in copyFrom)
- {
- if (c != null)
- {
- Type type = c.GetType();
- var copy = UnityEditor.Undo.AddComponent(gameObject, type);
- UnityEditor.Undo.RecordObject(copy, "copying pipeline");
- System.Reflection.BindingFlags bindingAttr
- = System.Reflection.BindingFlags.Public
- | System.Reflection.BindingFlags.NonPublic
- | System.Reflection.BindingFlags.Instance;
- System.Reflection.FieldInfo[] fields = type.GetFields(bindingAttr);
- for (int i = 0; i < fields.Length; ++i)
- if (!fields[i].IsStatic)
- fields[i].SetValue(copy, fields[i].GetValue(c));
- }
- }
- }
- #endif
- /// Legacy support for an old API. GML todo: deprecate these methods
- /// <summary>Get the component set for a specific stage.</summary>
- /// <param name="stage">The stage for which we want the component</param>
- /// <returns>The Cinemachine component for that stage, or null if not defined</returns>
- public CinemachineComponentBase GetCinemachineComponent(CinemachineCore.Stage stage)
- {
- var cache = ComponentCache;
- var i = (int)stage;
- return i >= 0 && i < cache.Length ? cache[i] : null;
- }
- /// <summary>Get an existing component of a specific type from the cinemachine pipeline.</summary>
- public T GetCinemachineComponent<T>() where T : CinemachineComponentBase
- {
- var components = ComponentCache;
- foreach (var c in components)
- if (c is T)
- return c as T;
- return null;
- }
- /// <summary>Add a component to the cinemachine pipeline.</summary>
- public T AddCinemachineComponent<T>() where T : CinemachineComponentBase
- {
- var components = ComponentCache;
- T c = gameObject.AddComponent<T>();
- var oldC = components[(int)c.Stage];
- if (oldC != null)
- {
- oldC.enabled = false;
- RuntimeUtility.DestroyObject(oldC);
- }
- InvalidateComponentCache();
- return c;
- }
- /// <summary>Remove a component from the cinemachine pipeline.</summary>
- public void DestroyCinemachineComponent<T>() where T : CinemachineComponentBase
- {
- var components = ComponentCache;
- foreach (var c in components)
- {
- if (c is T)
- {
- c.enabled = false;
- RuntimeUtility.DestroyObject(c);
- InvalidateComponentCache();
- return;
- }
- }
- }
- }
- }
- #endif
|