329 lines
14 KiB
C#
329 lines
14 KiB
C#
|
|
using System;
|
||
|
|
using System.Collections.Generic;
|
||
|
|
using UnityEngine;
|
||
|
|
using UnityEngine.Serialization;
|
||
|
|
|
||
|
|
namespace Unity.Cinemachine
|
||
|
|
{
|
||
|
|
/// <summary>
|
||
|
|
/// This is a virtual camera "manager" that owns and manages a collection
|
||
|
|
/// of child Cm Cameras.
|
||
|
|
/// </summary>
|
||
|
|
public abstract class CinemachineCameraManagerBase : CinemachineVirtualCameraBase, ICinemachineMixer
|
||
|
|
{
|
||
|
|
/// <summary>If enabled, a default target will be available. It will be used
|
||
|
|
/// if a child rig needs a target and doesn't specify one itself.</summary>
|
||
|
|
[Serializable]
|
||
|
|
public struct DefaultTargetSettings
|
||
|
|
{
|
||
|
|
/// <summary>If enabled, a default target will be available. It will be used
|
||
|
|
/// if a child rig needs a target and doesn't specify one itself.</summary>
|
||
|
|
[Tooltip("If enabled, a default target will be available. It will be used "
|
||
|
|
+ "if a child rig needs a target and doesn't specify one itself.")]
|
||
|
|
public bool Enabled;
|
||
|
|
|
||
|
|
/// <summary>Default target for the camera children, which may be used if the child rig
|
||
|
|
/// does not specify a target of its own.</summary>
|
||
|
|
[NoSaveDuringPlay]
|
||
|
|
[Tooltip("Default target for the camera children, which may be used if the child rig "
|
||
|
|
+ "does not specify a target of its own.")]
|
||
|
|
public CameraTarget Target;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>If enabled, a default target will be available. It will be used
|
||
|
|
/// if a child rig needs a target and doesn't specify one itself.</summary>
|
||
|
|
[FoldoutWithEnabledButton]
|
||
|
|
public DefaultTargetSettings DefaultTarget;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// The blend which is used if you don't explicitly define a blend between two Virtual Camera children.
|
||
|
|
/// </summary>
|
||
|
|
[Tooltip("The blend which is used if you don't explicitly define a blend between two Virtual Camera children")]
|
||
|
|
[FormerlySerializedAs("m_DefaultBlend")]
|
||
|
|
public CinemachineBlendDefinition DefaultBlend = new (CinemachineBlendDefinition.Styles.EaseInOut, 0.5f);
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// This is the asset which contains custom settings for specific child blends.
|
||
|
|
/// </summary>
|
||
|
|
[Tooltip("This is the asset which contains custom settings for specific child blends")]
|
||
|
|
[FormerlySerializedAs("m_CustomBlends")]
|
||
|
|
[EmbeddedBlenderSettingsProperty]
|
||
|
|
public CinemachineBlenderSettings CustomBlends = null;
|
||
|
|
|
||
|
|
List<CinemachineVirtualCameraBase> m_ChildCameras;
|
||
|
|
int m_ChildCountCache; // used for invalidating child cache
|
||
|
|
readonly BlendManager m_BlendManager = new ();
|
||
|
|
CameraState m_State = CameraState.Default;
|
||
|
|
ICinemachineCamera m_TransitioningFrom;
|
||
|
|
|
||
|
|
/// <summary>Reset the component to default values.</summary>
|
||
|
|
protected virtual void Reset()
|
||
|
|
{
|
||
|
|
Priority = default;
|
||
|
|
OutputChannel = OutputChannels.Default;
|
||
|
|
DefaultTarget = default;
|
||
|
|
InvalidateCameraCache();
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Standard MonoBehaviour OnEnable. Derived classes must call base class implementation.
|
||
|
|
/// </summary>
|
||
|
|
protected override void OnEnable()
|
||
|
|
{
|
||
|
|
base.OnEnable();
|
||
|
|
m_BlendManager.OnEnable();
|
||
|
|
m_BlendManager.LookupBlendDelegate = LookupBlend;
|
||
|
|
InvalidateCameraCache();
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Standard MonoBehaviour OnDisable. Derived classes must call base class implementation.
|
||
|
|
/// </summary>
|
||
|
|
protected override void OnDisable()
|
||
|
|
{
|
||
|
|
m_BlendManager.OnDisable();
|
||
|
|
base.OnDisable();
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <inheritdoc />
|
||
|
|
public override string Description => m_BlendManager.Description;
|
||
|
|
|
||
|
|
/// <inheritdoc />
|
||
|
|
public override CameraState State => m_State;
|
||
|
|
|
||
|
|
/// <inheritdoc />
|
||
|
|
public virtual bool IsLiveChild(ICinemachineCamera cam, bool dominantChildOnly = false)
|
||
|
|
=> m_BlendManager.IsLive(cam);
|
||
|
|
|
||
|
|
/// <summary>The list of child cameras. These are just the immediate children in the hierarchy.</summary>
|
||
|
|
public List<CinemachineVirtualCameraBase> ChildCameras
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
UpdateCameraCache();
|
||
|
|
return m_ChildCameras;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <inheritdoc />
|
||
|
|
public override bool PreviousStateIsValid
|
||
|
|
{
|
||
|
|
get => base.PreviousStateIsValid;
|
||
|
|
set
|
||
|
|
{
|
||
|
|
base.PreviousStateIsValid = value;
|
||
|
|
// Only propagate to the children when we're invalidating the state
|
||
|
|
if (value == false)
|
||
|
|
for (int i = 0; m_ChildCameras != null && i < m_ChildCameras.Count; ++i)
|
||
|
|
m_ChildCameras[i].PreviousStateIsValid = value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>Is there a blend in progress?</summary>
|
||
|
|
public bool IsBlending => m_BlendManager.IsBlending;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Get the current blend in progress. Returns null if none.
|
||
|
|
/// It is also possible to set the current blend, but this is not a recommended usage
|
||
|
|
/// unless it is to set the active blend to null, which will force completion of the blend.
|
||
|
|
/// </summary>
|
||
|
|
public CinemachineBlend ActiveBlend
|
||
|
|
{
|
||
|
|
get => PreviousStateIsValid ? m_BlendManager.ActiveBlend : null;
|
||
|
|
set => m_BlendManager.ActiveBlend = value;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Get the current active camera. Will return null if no camera is active.
|
||
|
|
/// </summary>
|
||
|
|
public ICinemachineCamera LiveChild => PreviousStateIsValid ? m_BlendManager.ActiveVirtualCamera : null;
|
||
|
|
|
||
|
|
/// <summary>Get the current LookAt target. Returns parent's LookAt if parent
|
||
|
|
/// is non-null and no specific LookAt defined for this camera</summary>
|
||
|
|
public override Transform LookAt
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
if (!DefaultTarget.Enabled)
|
||
|
|
return null;
|
||
|
|
return ResolveLookAt(DefaultTarget.Target.CustomLookAtTarget
|
||
|
|
? DefaultTarget.Target.LookAtTarget : DefaultTarget.Target.TrackingTarget);
|
||
|
|
}
|
||
|
|
set
|
||
|
|
{
|
||
|
|
DefaultTarget.Enabled = true;
|
||
|
|
DefaultTarget.Target.CustomLookAtTarget = true;
|
||
|
|
DefaultTarget.Target.LookAtTarget = 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>
|
||
|
|
public override Transform Follow
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
if (!DefaultTarget.Enabled)
|
||
|
|
return null;
|
||
|
|
return ResolveFollow(DefaultTarget.Target.TrackingTarget);
|
||
|
|
}
|
||
|
|
set
|
||
|
|
{
|
||
|
|
DefaultTarget.Enabled = true;
|
||
|
|
DefaultTarget.Target.TrackingTarget = value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>Internal use only. Do not call this method.
|
||
|
|
/// Called by CinemachineCore at designated update time
|
||
|
|
/// so the vcam can position itself and track its targets. This implementation
|
||
|
|
/// updates all the children, chooses the best one, and implements any required blending.</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 or equal to 0)</param>
|
||
|
|
public override void InternalUpdateCameraState(Vector3 worldUp, float deltaTime)
|
||
|
|
{
|
||
|
|
UpdateTargetCache();
|
||
|
|
UpdateCameraCache();
|
||
|
|
if (!PreviousStateIsValid)
|
||
|
|
ResetLiveChild();
|
||
|
|
|
||
|
|
// Choose the best camera - auto-activate it if it's inactive
|
||
|
|
var best = ChooseCurrentCamera(worldUp, deltaTime);
|
||
|
|
if (best != null && !best.gameObject.activeInHierarchy)
|
||
|
|
{
|
||
|
|
best.gameObject.SetActive(true);
|
||
|
|
best.UpdateCameraState(worldUp, deltaTime);
|
||
|
|
}
|
||
|
|
SetLiveChild(best, worldUp, deltaTime);
|
||
|
|
|
||
|
|
// Special case to handle being called from OnTransitionFromCamera() - GML todo: fix this
|
||
|
|
if (m_TransitioningFrom != null && !IsBlending && LiveChild != null)
|
||
|
|
{
|
||
|
|
LiveChild.OnCameraActivated(new ICinemachineCamera.ActivationEventParams
|
||
|
|
{
|
||
|
|
Origin = this,
|
||
|
|
OutgoingCamera = m_TransitioningFrom,
|
||
|
|
IncomingCamera = LiveChild,
|
||
|
|
IsCut = false,
|
||
|
|
WorldUp = worldUp,
|
||
|
|
DeltaTime = deltaTime
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
FinalizeCameraState(deltaTime);
|
||
|
|
m_TransitioningFrom = null;
|
||
|
|
PreviousStateIsValid = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>Find a blend curve for blending from one child camera to another.</summary>
|
||
|
|
/// <param name="outgoing">The camera we're blending from.</param>
|
||
|
|
/// <param name="incoming">The camera we're blending to.</param>
|
||
|
|
/// <returns>The blend to use for this camera transition.</returns>
|
||
|
|
protected virtual CinemachineBlendDefinition LookupBlend(ICinemachineCamera outgoing, ICinemachineCamera incoming)
|
||
|
|
{
|
||
|
|
return CinemachineBlenderSettings.LookupBlend(outgoing, incoming, DefaultBlend, CustomBlends, this);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Choose the appropriate current camera from among the ChildCameras, based on current state.
|
||
|
|
/// If the returned camera is different from the current camera, an appropriate transition will be made.
|
||
|
|
/// </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 or equal to 0)</param>
|
||
|
|
/// <returns>The current child camera that should be active. Must be present in ChildCameras.</returns>
|
||
|
|
protected abstract CinemachineVirtualCameraBase ChooseCurrentCamera(Vector3 worldUp, float deltaTime);
|
||
|
|
|
||
|
|
/// <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 seamlessly.</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)
|
||
|
|
{
|
||
|
|
UpdateCameraCache();
|
||
|
|
for (int i = 0; i < m_ChildCameras.Count; ++i)
|
||
|
|
m_ChildCameras[i].OnTargetObjectWarped(target, positionDelta);
|
||
|
|
base.OnTargetObjectWarped(target, positionDelta);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Force the virtual camera to assume a given position and orientation
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="pos">World-space position to take</param>
|
||
|
|
/// <param name="rot">World-space orientation to take</param>
|
||
|
|
public override void ForceCameraPosition(Vector3 pos, Quaternion rot)
|
||
|
|
{
|
||
|
|
UpdateCameraCache();
|
||
|
|
for (int i = 0; i < m_ChildCameras.Count; ++i)
|
||
|
|
m_ChildCameras[i].ForceCameraPosition(pos, rot);
|
||
|
|
base.ForceCameraPosition(pos, rot);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>Notification that this virtual camera is going live.</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);
|
||
|
|
m_TransitioningFrom = fromCam;
|
||
|
|
InvokeOnTransitionInExtensions(fromCam, worldUp, deltaTime);
|
||
|
|
InternalUpdateCameraState(worldUp, deltaTime);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>Force a rebuild of the child camera cache.
|
||
|
|
/// Call this if CinemachineCamera children are added or removed dynamically</summary>
|
||
|
|
public void InvalidateCameraCache()
|
||
|
|
{
|
||
|
|
m_ChildCameras = null;
|
||
|
|
PreviousStateIsValid = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>Rebuild the camera cache if it's been invalidated</summary>
|
||
|
|
/// <returns>True if a cache rebuild was performed, false if cache is up to date.</returns>
|
||
|
|
protected virtual bool UpdateCameraCache()
|
||
|
|
{
|
||
|
|
var childCount = transform.childCount;
|
||
|
|
if (m_ChildCameras != null && m_ChildCountCache == childCount)
|
||
|
|
return false;
|
||
|
|
PreviousStateIsValid = false;
|
||
|
|
m_ChildCameras = new();
|
||
|
|
m_ChildCountCache = childCount;
|
||
|
|
GetComponentsInChildren(true, m_ChildCameras);
|
||
|
|
for (int i = m_ChildCameras.Count-1; i >= 0; --i)
|
||
|
|
if (m_ChildCameras[i].transform.parent != transform)
|
||
|
|
m_ChildCameras.RemoveAt(i);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>Makes sure the internal child cache is up to date</summary>
|
||
|
|
protected virtual void OnTransformChildrenChanged() => InvalidateCameraCache();
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Set the current active camera. All necessary blends will be created, and events generated.
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="activeCamera">Current active camera</param>
|
||
|
|
/// <param name="worldUp">Current world up</param>
|
||
|
|
/// <param name="deltaTime">Current deltaTime applicable for this frame</param>
|
||
|
|
protected void SetLiveChild(
|
||
|
|
ICinemachineCamera activeCamera, Vector3 worldUp, float deltaTime)
|
||
|
|
{
|
||
|
|
m_BlendManager.UpdateRootFrame(this, activeCamera, worldUp, deltaTime);
|
||
|
|
m_BlendManager.ComputeCurrentBlend();
|
||
|
|
m_BlendManager.ProcessActiveCamera(this, worldUp, deltaTime);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>Cancel current active camera and all blends</summary>
|
||
|
|
protected void ResetLiveChild() => m_BlendManager.ResetRootFrame();
|
||
|
|
|
||
|
|
/// <summary>At the end of InternalUpdateCameraState, call this to finalize the state</summary>
|
||
|
|
/// <param name="deltaTime">Current deltaTime for this frame</param>
|
||
|
|
protected void FinalizeCameraState(float deltaTime)
|
||
|
|
{
|
||
|
|
m_State = m_BlendManager.CameraState;
|
||
|
|
InvokePostPipelineStageCallback(this, CinemachineCore.Stage.Finalize, ref m_State, deltaTime);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|