641 lines
29 KiB
C#
641 lines
29 KiB
C#
|
using System.Collections.Generic;
|
|||
|
using System.Linq;
|
|||
|
using UnityEngine;
|
|||
|
using UnityEngine.Serialization;
|
|||
|
|
|||
|
namespace Cinemachine
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// This behaviour is intended to be attached to an empty Transform GameObject,
|
|||
|
/// and it represents a Virtual Camera within the Unity scene.
|
|||
|
///
|
|||
|
/// The Virtual Camera will animate its Transform according to the rules contained
|
|||
|
/// in its CinemachineComponent pipeline (Aim, Body, and Noise). When the virtual
|
|||
|
/// camera is Live, the Unity camera will assume the position and orientation
|
|||
|
/// of the virtual camera.
|
|||
|
///
|
|||
|
/// A virtual camera is not a camera. Instead, it can be thought of as a camera controller,
|
|||
|
/// not unlike a cameraman. It can drive the Unity Camera and control its position,
|
|||
|
/// orientation, lens settings, and PostProcessing effects. Each Virtual Camera owns
|
|||
|
/// its own Cinemachine Component Pipeline, through which you provide the instructions
|
|||
|
/// for dynamically tracking specific game objects.
|
|||
|
///
|
|||
|
/// A virtual camera is very lightweight, and does no rendering of its own. It merely
|
|||
|
/// tracks interesting GameObjects, and positions itself accordingly. A typical game
|
|||
|
/// can have dozens of virtual cameras, each set up to follow a particular character
|
|||
|
/// or capture a particular event.
|
|||
|
///
|
|||
|
/// A Virtual Camera can be in any of three states:
|
|||
|
///
|
|||
|
/// * **Live**: The virtual camera is actively controlling the Unity Camera. The
|
|||
|
/// virtual camera is tracking its targets and being updated every frame.
|
|||
|
/// * **Standby**: The virtual camera is tracking its targets and being updated
|
|||
|
/// every frame, but no Unity Camera is actively being controlled by it. This is
|
|||
|
/// the state of a virtual camera that is enabled in the scene but perhaps at a
|
|||
|
/// lower priority than the Live virtual camera.
|
|||
|
/// * **Disabled**: The virtual camera is present but disabled in the scene. It is
|
|||
|
/// not actively tracking its targets and so consumes no processing power. However,
|
|||
|
/// the virtual camera can be made live from the Timeline.
|
|||
|
///
|
|||
|
/// The Unity Camera can be driven by any virtual camera in the scene. The game
|
|||
|
/// logic can choose the virtual camera to make live by manipulating the virtual
|
|||
|
/// cameras' enabled flags and their priorities, based on game logic.
|
|||
|
///
|
|||
|
/// In order to be driven by a virtual camera, the Unity Camera must have a CinemachineBrain
|
|||
|
/// behaviour, which will select the most eligible virtual camera based on its priority
|
|||
|
/// or on other criteria, and will manage blending.
|
|||
|
/// </summary>
|
|||
|
/// <seealso cref="CinemachineVirtualCameraBase"/>
|
|||
|
/// <seealso cref="LensSettings"/>
|
|||
|
/// <seealso cref="CinemachineComposer"/>
|
|||
|
/// <seealso cref="CinemachineTransposer"/>
|
|||
|
/// <seealso cref="CinemachineBasicMultiChannelPerlin"/>
|
|||
|
[DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)]
|
|||
|
[DisallowMultipleComponent]
|
|||
|
[ExecuteAlways]
|
|||
|
[ExcludeFromPreset]
|
|||
|
[AddComponentMenu("Cinemachine/CinemachineVirtualCamera")]
|
|||
|
[HelpURL(Documentation.BaseURL + "manual/CinemachineVirtualCamera.html")]
|
|||
|
public class CinemachineVirtualCamera : CinemachineVirtualCameraBase
|
|||
|
{
|
|||
|
/// <summary>The object that the camera wants to look at (the Aim target).
|
|||
|
/// The Aim component of the CinemachineComponent pipeline
|
|||
|
/// will refer to this target and orient the vcam in accordance with rules and
|
|||
|
/// settings that are provided to it.
|
|||
|
/// If this is null, then the vcam's Transform orientation will be used.</summary>
|
|||
|
[Tooltip("The object that the camera wants to look at (the Aim target). "
|
|||
|
+ "If this is null, then the vcam's Transform orientation will define the camera's orientation.")]
|
|||
|
[NoSaveDuringPlay]
|
|||
|
[VcamTargetProperty]
|
|||
|
public Transform m_LookAt = null;
|
|||
|
|
|||
|
/// <summary>The object that the camera wants to move with (the Body target).
|
|||
|
/// The Body component of the CinemachineComponent pipeline
|
|||
|
/// will refer to this target and position the vcam in accordance with rules and
|
|||
|
/// settings that are provided to it.
|
|||
|
/// If this is null, then the vcam's Transform position will be used.</summary>
|
|||
|
[Tooltip("The object that the camera wants to move with (the Body target). "
|
|||
|
+ "If this is null, then the vcam's Transform position will define the camera's position.")]
|
|||
|
[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>
|
|||
|
[FormerlySerializedAs("m_LensAttributes")]
|
|||
|
[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.")]
|
|||
|
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>Legacy support</summary>
|
|||
|
[SerializeField] [HideInInspector]
|
|||
|
[FormerlySerializedAs("m_BlendHint")]
|
|||
|
[FormerlySerializedAs("m_PositionBlending")] private BlendHint m_LegacyBlendHint;
|
|||
|
|
|||
|
/// <summary>This is the name of the hidden GameObject that will be created as a child object
|
|||
|
/// of the virtual camera. This hidden game object acts as a container for the polymorphic
|
|||
|
/// CinemachineComponent pipeline. The Inspector UI for the Virtual Camera
|
|||
|
/// provides access to this pipleline, as do the CinemachineComponent-family of
|
|||
|
/// public methods in this class.
|
|||
|
/// The lifecycle of the pipeline GameObject is managed automatically.</summary>
|
|||
|
public const string PipelineName = "cm";
|
|||
|
|
|||
|
/// <summary>The CameraState object holds all of the information
|
|||
|
/// necessary to position the Unity camera. It is the output of this class.</summary>
|
|||
|
override public CameraState State { get { return m_State; } }
|
|||
|
|
|||
|
/// <summary>Get the LookAt target for the Aim component in the Cinemachine pipeline.
|
|||
|
/// If this vcam is a part of a meta-camera collection, then the owner's target
|
|||
|
/// will be used if the local target is null.</summary>
|
|||
|
override public Transform LookAt
|
|||
|
{
|
|||
|
get { return ResolveLookAt(m_LookAt); }
|
|||
|
set { m_LookAt = value; }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Get the Follow target for the Body component in the Cinemachine pipeline.
|
|||
|
/// If this vcam is a part of a meta-camera collection, then the owner's target
|
|||
|
/// will be used if the local target is null.</summary>
|
|||
|
override public Transform Follow
|
|||
|
{
|
|||
|
get { return ResolveFollow(m_Follow); }
|
|||
|
set { m_Follow = value; }
|
|||
|
}
|
|||
|
|
|||
|
/// <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();
|
|||
|
UpdateComponentPipeline();
|
|||
|
if (m_ComponentPipeline != null)
|
|||
|
for (int i = 0; i < m_ComponentPipeline.Length; ++i)
|
|||
|
maxDamp = Mathf.Max(maxDamp, m_ComponentPipeline[i].GetMaxDampTime());
|
|||
|
return maxDamp;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Internal use only. Do not call this method.
|
|||
|
/// Called by CinemachineCore at the appropriate Update time
|
|||
|
/// so the vcam can position itself and track its targets. This class will
|
|||
|
/// invoke its pipeline and generate a CameraState for this frame.</summary>
|
|||
|
/// <param name="worldUp">Effective world up</param>
|
|||
|
/// <param name="deltaTime">Effective deltaTime</param>
|
|||
|
override public void InternalUpdateCameraState(Vector3 worldUp, float deltaTime)
|
|||
|
{
|
|||
|
UpdateTargetCache();
|
|||
|
|
|||
|
// Update the state by invoking the component pipeline
|
|||
|
m_State = CalculateNewState(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 (!m_UserIsDragging)
|
|||
|
{
|
|||
|
if (Follow != null)
|
|||
|
transform.position = State.RawPosition;
|
|||
|
if (LookAt != null)
|
|||
|
transform.rotation = State.RawOrientation;
|
|||
|
}
|
|||
|
PreviousStateIsValid = true;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Make sure that the pipeline cache is up-to-date.</summary>
|
|||
|
override protected void OnEnable()
|
|||
|
{
|
|||
|
base.OnEnable();
|
|||
|
m_State = PullStateFromVirtualCamera(Vector3.up, ref m_Lens);
|
|||
|
InvalidateComponentPipeline();
|
|||
|
|
|||
|
// Can't add components during OnValidate
|
|||
|
if (ValidatingStreamVersion < 20170927)
|
|||
|
{
|
|||
|
if (Follow != null && GetCinemachineComponent(CinemachineCore.Stage.Body) == null)
|
|||
|
AddCinemachineComponent<CinemachineHardLockToTarget>();
|
|||
|
if (LookAt != null && GetCinemachineComponent(CinemachineCore.Stage.Aim) == null)
|
|||
|
AddCinemachineComponent<CinemachineHardLookAt>();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Calls the DestroyPipelineDelegate for destroying the hidden
|
|||
|
/// child object, to support undo.</summary>
|
|||
|
protected override void OnDestroy()
|
|||
|
{
|
|||
|
// Make the pipeline visible instead of destroying - this is to keep Undo happy
|
|||
|
foreach (Transform child in transform)
|
|||
|
if (child.GetComponent<CinemachinePipeline>() != null)
|
|||
|
child.gameObject.hideFlags
|
|||
|
&= ~(HideFlags.HideInHierarchy | HideFlags.HideInInspector);
|
|||
|
|
|||
|
base.OnDestroy();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Enforce bounds for fields, when changed in inspector.</summary>
|
|||
|
protected override void OnValidate()
|
|||
|
{
|
|||
|
base.OnValidate();
|
|||
|
m_Lens.Validate();
|
|||
|
if (m_LegacyBlendHint != BlendHint.None)
|
|||
|
{
|
|||
|
m_Transitions.m_BlendHint = m_LegacyBlendHint;
|
|||
|
m_LegacyBlendHint = BlendHint.None;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void OnTransformChildrenChanged()
|
|||
|
{
|
|||
|
InvalidateComponentPipeline();
|
|||
|
}
|
|||
|
|
|||
|
void Reset()
|
|||
|
{
|
|||
|
#if UNITY_EDITOR
|
|||
|
if (UnityEditor.PrefabUtility.GetPrefabInstanceStatus(gameObject)
|
|||
|
!= UnityEditor.PrefabInstanceStatus.NotAPrefab)
|
|||
|
{
|
|||
|
Debug.Log("You cannot reset a prefab instance. "
|
|||
|
+ "First disconnect this instance from the prefab, or enter Prefab Edit mode");
|
|||
|
return;
|
|||
|
}
|
|||
|
#endif
|
|||
|
DestroyPipeline();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Override component pipeline creation.
|
|||
|
/// This needs to be done by the editor to support Undo.
|
|||
|
/// The override must do exactly the same thing as the CreatePipeline method in this class.
|
|||
|
/// </summary>
|
|||
|
public static CreatePipelineDelegate CreatePipelineOverride;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Override component pipeline creation.
|
|||
|
/// This needs to be done by the editor to support Undo.
|
|||
|
/// The override must do exactly the same thing as the CreatePipeline method in
|
|||
|
/// the CinemachineVirtualCamera class.
|
|||
|
/// </summary>
|
|||
|
public delegate Transform CreatePipelineDelegate(
|
|||
|
CinemachineVirtualCamera vcam, string name, CinemachineComponentBase[] copyFrom);
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Override component pipeline destruction.
|
|||
|
/// This needs to be done by the editor to support Undo.
|
|||
|
/// </summary>
|
|||
|
public static DestroyPipelineDelegate DestroyPipelineOverride;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Override component pipeline destruction.
|
|||
|
/// This needs to be done by the editor to support Undo.
|
|||
|
/// </summary>
|
|||
|
public delegate void DestroyPipelineDelegate(GameObject pipeline);
|
|||
|
|
|||
|
/// <summary>Destroy any existing pipeline container.</summary>
|
|||
|
private void DestroyPipeline()
|
|||
|
{
|
|||
|
List<Transform> oldPipeline = new List<Transform>();
|
|||
|
foreach (Transform child in transform)
|
|||
|
if (child.GetComponent<CinemachinePipeline>() != null)
|
|||
|
oldPipeline.Add(child);
|
|||
|
|
|||
|
if (!RuntimeUtility.IsPrefab(gameObject))
|
|||
|
{
|
|||
|
foreach (Transform child in oldPipeline)
|
|||
|
{
|
|||
|
if (DestroyPipelineOverride != null)
|
|||
|
DestroyPipelineOverride(child.gameObject);
|
|||
|
else
|
|||
|
Destroy(child.gameObject);
|
|||
|
}
|
|||
|
m_ComponentOwner = null;
|
|||
|
}
|
|||
|
PreviousStateIsValid = false;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Create a default pipeline container.
|
|||
|
/// Note: copyFrom only supported in Editor, not build</summary>
|
|||
|
private Transform CreatePipeline(CinemachineVirtualCamera copyFrom)
|
|||
|
{
|
|||
|
CinemachineComponentBase[] components = null;
|
|||
|
if (copyFrom != null)
|
|||
|
{
|
|||
|
copyFrom.InvalidateComponentPipeline(); // make sure it's up to date
|
|||
|
components = copyFrom.GetComponentPipeline();
|
|||
|
}
|
|||
|
|
|||
|
Transform newPipeline = null;
|
|||
|
if (CreatePipelineOverride != null)
|
|||
|
newPipeline = CreatePipelineOverride(this, PipelineName, components);
|
|||
|
else
|
|||
|
{
|
|||
|
GameObject go = new GameObject(PipelineName);
|
|||
|
go.transform.parent = transform;
|
|||
|
go.AddComponent<CinemachinePipeline>();
|
|||
|
newPipeline = go.transform;
|
|||
|
}
|
|||
|
PreviousStateIsValid = false;
|
|||
|
return newPipeline;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Editor API: Call this when changing the pipeline from the editor.
|
|||
|
/// Will force a rebuild of the pipeline cache.
|
|||
|
/// </summary>
|
|||
|
public void InvalidateComponentPipeline() { m_ComponentPipeline = null; }
|
|||
|
|
|||
|
/// <summary>Get the hidden CinemachinePipeline child object.</summary>
|
|||
|
/// <returns>The hidden CinemachinePipeline child object</returns>
|
|||
|
public Transform GetComponentOwner() { UpdateComponentPipeline(); return m_ComponentOwner; }
|
|||
|
|
|||
|
/// <summary>Get the component pipeline owned by the hidden child pipline container.
|
|||
|
/// For most purposes, it is preferable to use the GetCinemachineComponent method.</summary>
|
|||
|
/// <returns>The component pipeline</returns>
|
|||
|
public CinemachineComponentBase[] GetComponentPipeline() { UpdateComponentPipeline(); return m_ComponentPipeline; }
|
|||
|
|
|||
|
/// <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)
|
|||
|
{
|
|||
|
CinemachineComponentBase[] components = GetComponentPipeline();
|
|||
|
if (components != null)
|
|||
|
foreach (var c in components)
|
|||
|
if (c.Stage == stage)
|
|||
|
return c;
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Get an existing component of a specific type from the cinemachine pipeline.</summary>
|
|||
|
/// <typeparam name="T">The type of component to get</typeparam>
|
|||
|
/// <returns>The component if it's present, or null</returns>
|
|||
|
public T GetCinemachineComponent<T>() where T : CinemachineComponentBase
|
|||
|
{
|
|||
|
CinemachineComponentBase[] components = GetComponentPipeline();
|
|||
|
if (components != null)
|
|||
|
foreach (var c in components)
|
|||
|
if (c is T)
|
|||
|
return c as T;
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Add a component to the cinemachine pipeline.
|
|||
|
/// Existing components at the new component's stage are removed</summary>
|
|||
|
/// <typeparam name="T">The type of component to add</typeparam>
|
|||
|
/// <returns>The new component</returns>
|
|||
|
public T AddCinemachineComponent<T>() where T : CinemachineComponentBase
|
|||
|
{
|
|||
|
// Get the existing components
|
|||
|
Transform owner = GetComponentOwner();
|
|||
|
if (owner == null)
|
|||
|
return null; // maybe it's a prefab
|
|||
|
CinemachineComponentBase[] components = owner.GetComponents<CinemachineComponentBase>();
|
|||
|
|
|||
|
T component = owner.gameObject.AddComponent<T>();
|
|||
|
if (component != null && components != null)
|
|||
|
{
|
|||
|
// Remove the existing components at that stage
|
|||
|
CinemachineCore.Stage stage = component.Stage;
|
|||
|
for (int i = components.Length - 1; i >= 0; --i)
|
|||
|
{
|
|||
|
if (components[i].Stage == stage)
|
|||
|
{
|
|||
|
components[i].enabled = false;
|
|||
|
RuntimeUtility.DestroyObject(components[i]);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
InvalidateComponentPipeline();
|
|||
|
return component;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Remove a component from the cinemachine pipeline if it's present.</summary>
|
|||
|
/// <typeparam name="T">The type of component to remove</typeparam>
|
|||
|
public void DestroyCinemachineComponent<T>() where T : CinemachineComponentBase
|
|||
|
{
|
|||
|
CinemachineComponentBase[] components = GetComponentPipeline();
|
|||
|
if (components != null)
|
|||
|
{
|
|||
|
foreach (var c in components)
|
|||
|
{
|
|||
|
if (c is T)
|
|||
|
{
|
|||
|
c.enabled = false;
|
|||
|
RuntimeUtility.DestroyObject(c);
|
|||
|
InvalidateComponentPipeline();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>API for the editor, to make the dragging of position handles behave better.</summary>
|
|||
|
internal bool m_UserIsDragging;
|
|||
|
|
|||
|
CameraState m_State = CameraState.Default; // Current state this frame
|
|||
|
|
|||
|
CinemachineComponentBase[] m_ComponentPipeline = null;
|
|||
|
[SerializeField][HideInInspector] private Transform m_ComponentOwner = null; // serialized to handle copy/paste
|
|||
|
void UpdateComponentPipeline()
|
|||
|
{
|
|||
|
bool isPrefab = RuntimeUtility.IsPrefab(gameObject);
|
|||
|
#if UNITY_EDITOR
|
|||
|
// Did we just get copy/pasted?
|
|||
|
if (m_ComponentOwner != null && m_ComponentOwner.parent != transform)
|
|||
|
{
|
|||
|
if (!isPrefab) // can't paste to a prefab
|
|||
|
{
|
|||
|
CinemachineVirtualCamera copyFrom = (m_ComponentOwner.parent != null)
|
|||
|
? m_ComponentOwner.parent.gameObject.GetComponent<CinemachineVirtualCamera>() : null;
|
|||
|
DestroyPipeline();
|
|||
|
m_ComponentOwner = CreatePipeline(copyFrom);
|
|||
|
}
|
|||
|
}
|
|||
|
if (m_ComponentOwner != null)
|
|||
|
SetFlagsForHiddenChild(m_ComponentOwner.gameObject);
|
|||
|
#endif
|
|||
|
// Early out if we're up-to-date
|
|||
|
if (m_ComponentOwner != null && m_ComponentPipeline != null)
|
|||
|
return;
|
|||
|
|
|||
|
m_ComponentOwner = null;
|
|||
|
List<CinemachineComponentBase> list = new List<CinemachineComponentBase>();
|
|||
|
foreach (Transform child in transform)
|
|||
|
{
|
|||
|
if (child.GetComponent<CinemachinePipeline>() != null)
|
|||
|
{
|
|||
|
m_ComponentOwner = child;
|
|||
|
CinemachineComponentBase[] components = child.GetComponents<CinemachineComponentBase>();
|
|||
|
foreach (CinemachineComponentBase c in components)
|
|||
|
if (c.enabled)
|
|||
|
list.Add(c);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Make sure we have a pipeline owner
|
|||
|
if (m_ComponentOwner == null && !isPrefab)
|
|||
|
m_ComponentOwner = CreatePipeline(null);
|
|||
|
|
|||
|
// Make sure the pipeline stays hidden, even through prefab
|
|||
|
if (m_ComponentOwner != null)
|
|||
|
SetFlagsForHiddenChild(m_ComponentOwner.gameObject);
|
|||
|
if (m_ComponentOwner != null && m_ComponentOwner.gameObject != null)
|
|||
|
{
|
|||
|
// Sort the pipeline
|
|||
|
list.Sort((c1, c2) => (int)c1.Stage - (int)c2.Stage);
|
|||
|
m_ComponentPipeline = list.ToArray();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static internal void SetFlagsForHiddenChild(GameObject child)
|
|||
|
{
|
|||
|
if (child != null)
|
|||
|
{
|
|||
|
if (CinemachineCore.sShowHiddenObjects)
|
|||
|
child.hideFlags &= ~(HideFlags.HideInHierarchy | HideFlags.HideInInspector);
|
|||
|
else
|
|||
|
child.hideFlags |= (HideFlags.HideInHierarchy | HideFlags.HideInInspector);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private Transform mCachedLookAtTarget;
|
|||
|
private CinemachineVirtualCameraBase mCachedLookAtTargetVcam;
|
|||
|
private CameraState CalculateNewState(Vector3 worldUp, float deltaTime)
|
|||
|
{
|
|||
|
FollowTargetAttachment = 1;
|
|||
|
LookAtTargetAttachment = 1;
|
|||
|
|
|||
|
// Initialize the camera state, in case the game object got moved in the editor
|
|||
|
CameraState state = PullStateFromVirtualCamera(worldUp, ref m_Lens);
|
|||
|
|
|||
|
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);
|
|||
|
}
|
|||
|
|
|||
|
// Update the state by invoking the component pipeline
|
|||
|
UpdateComponentPipeline(); // avoid GetComponentPipeline() here because of GC
|
|||
|
|
|||
|
// Extensions first
|
|||
|
InvokePrePipelineMutateCameraStateCallback(this, ref state, deltaTime);
|
|||
|
|
|||
|
// Then components
|
|||
|
if (m_ComponentPipeline == null)
|
|||
|
{
|
|||
|
state.BlendHint |= CameraState.BlendHintValue.IgnoreLookAtTarget;
|
|||
|
for (var stage = CinemachineCore.Stage.Body; stage <= CinemachineCore.Stage.Finalize; ++stage)
|
|||
|
InvokePostPipelineStageCallback(this, stage, ref state, deltaTime);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
for (int i = 0; i < m_ComponentPipeline.Length; ++i)
|
|||
|
if (m_ComponentPipeline[i] != null)
|
|||
|
m_ComponentPipeline[i].PrePipelineMutateCameraState(ref state, deltaTime);
|
|||
|
|
|||
|
int componentIndex = 0;
|
|||
|
CinemachineComponentBase postAimBody = null;
|
|||
|
for (var stage = CinemachineCore.Stage.Body; stage <= CinemachineCore.Stage.Finalize; ++stage)
|
|||
|
{
|
|||
|
var c = componentIndex < m_ComponentPipeline.Length
|
|||
|
? m_ComponentPipeline[componentIndex] : null;
|
|||
|
if (c != null && stage == c.Stage)
|
|||
|
{
|
|||
|
++componentIndex;
|
|||
|
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;
|
|||
|
// 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;
|
|||
|
}
|
|||
|
|
|||
|
/// <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;
|
|||
|
}
|
|||
|
UpdateComponentPipeline(); // avoid GetComponentPipeline() here because of GC
|
|||
|
if (m_ComponentPipeline != null)
|
|||
|
{
|
|||
|
for (int i = 0; i < m_ComponentPipeline.Length; ++i)
|
|||
|
m_ComponentPipeline[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 = true;
|
|||
|
transform.position = pos;
|
|||
|
transform.rotation = rot;
|
|||
|
m_State.RawPosition = pos;
|
|||
|
m_State.RawOrientation = rot;
|
|||
|
|
|||
|
UpdateComponentPipeline(); // avoid GetComponentPipeline() here because of GC
|
|||
|
if (m_ComponentPipeline != null)
|
|||
|
for (int i = 0; i < m_ComponentPipeline.Length; ++i)
|
|||
|
m_ComponentPipeline[i].ForceCameraPosition(pos, rot);
|
|||
|
|
|||
|
base.ForceCameraPosition(pos, rot);
|
|||
|
}
|
|||
|
|
|||
|
// This is a hack for FreeLook rigs - to be removed
|
|||
|
internal void SetStateRawPosition(Vector3 pos) { m_State.RawPosition = pos; }
|
|||
|
|
|||
|
/// <summary>If we are transitioning from another vcam, grab the position 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
|
|||
|
&& !CinemachineCore.Instance.IsLiveInBlend(this))
|
|||
|
ForceCameraPosition(fromCam.State.FinalPosition, fromCam.State.FinalOrientation);
|
|||
|
|
|||
|
UpdateComponentPipeline(); // avoid GetComponentPipeline() here because of GC
|
|||
|
if (m_ComponentPipeline != null)
|
|||
|
{
|
|||
|
for (int i = 0; i < m_ComponentPipeline.Length; ++i)
|
|||
|
if (m_ComponentPipeline[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>
|
|||
|
/// Returns true, when the vcam has an extension or components that require input.
|
|||
|
/// </summary>
|
|||
|
internal override bool RequiresUserInput()
|
|||
|
{
|
|||
|
if (base.RequiresUserInput())
|
|||
|
return true;
|
|||
|
return m_ComponentPipeline != null && m_ComponentPipeline.Any(c => c != null && c.RequiresUserInput);
|
|||
|
}
|
|||
|
|
|||
|
// This prevents the sensor size from dirtying the scene in the event of aspect ratio change
|
|||
|
internal override void OnBeforeSerialize()
|
|||
|
{
|
|||
|
if (!m_Lens.IsPhysicalCamera)
|
|||
|
m_Lens.SensorSize = Vector2.one;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|