301 lines
13 KiB
C#
301 lines
13 KiB
C#
|
using UnityEngine;
|
|||
|
using Cinemachine.Utility;
|
|||
|
using System.Collections.Generic;
|
|||
|
|
|||
|
namespace Cinemachine
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// CinemachineMixingCamera is a "manager camera" that takes on the state of
|
|||
|
/// the weighted average of the states of its child virtual cameras.
|
|||
|
///
|
|||
|
/// A fixed number of slots are made available for cameras, rather than a dynamic array.
|
|||
|
/// We do it this way in order to support weight animation from the Timeline.
|
|||
|
/// Timeline cannot animate array elements.
|
|||
|
/// </summary>
|
|||
|
[DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)]
|
|||
|
[DisallowMultipleComponent]
|
|||
|
[ExecuteAlways]
|
|||
|
[ExcludeFromPreset]
|
|||
|
[AddComponentMenu("Cinemachine/CinemachineMixingCamera")]
|
|||
|
[HelpURL(Documentation.BaseURL + "manual/CinemachineMixingCamera.html")]
|
|||
|
public class CinemachineMixingCamera : CinemachineVirtualCameraBase
|
|||
|
{
|
|||
|
/// <summary>The maximum number of tracked cameras. If you want to add
|
|||
|
/// more cameras, do it here in the source code, and be sure to add the
|
|||
|
/// extra member variables and to make the appropriate changes in
|
|||
|
/// GetWeight() and SetWeight().
|
|||
|
/// The inspector will figure itself out based on this value.</summary>
|
|||
|
public const int MaxCameras = 8;
|
|||
|
|
|||
|
/// <summary>Weight of the first tracked camera</summary>
|
|||
|
[Tooltip("The weight of the first tracked camera")]
|
|||
|
public float m_Weight0 = 0.5f;
|
|||
|
/// <summary>Weight of the second tracked camera</summary>
|
|||
|
[Tooltip("The weight of the second tracked camera")]
|
|||
|
public float m_Weight1 = 0.5f;
|
|||
|
/// <summary>Weight of the third tracked camera</summary>
|
|||
|
[Tooltip("The weight of the third tracked camera")]
|
|||
|
public float m_Weight2 = 0.5f;
|
|||
|
/// <summary>Weight of the fourth tracked camera</summary>
|
|||
|
[Tooltip("The weight of the fourth tracked camera")]
|
|||
|
public float m_Weight3 = 0.5f;
|
|||
|
/// <summary>Weight of the fifth tracked camera</summary>
|
|||
|
[Tooltip("The weight of the fifth tracked camera")]
|
|||
|
public float m_Weight4 = 0.5f;
|
|||
|
/// <summary>Weight of the sixth tracked camera</summary>
|
|||
|
[Tooltip("The weight of the sixth tracked camera")]
|
|||
|
public float m_Weight5 = 0.5f;
|
|||
|
/// <summary>Weight of the seventh tracked camera</summary>
|
|||
|
[Tooltip("The weight of the seventh tracked camera")]
|
|||
|
public float m_Weight6 = 0.5f;
|
|||
|
/// <summary>Weight of the eighth tracked camera</summary>
|
|||
|
[Tooltip("The weight of the eighth tracked camera")]
|
|||
|
public float m_Weight7 = 0.5f;
|
|||
|
|
|||
|
/// <summary>Get the weight of the child at an index.</summary>
|
|||
|
/// <param name="index">The child index. Only immediate CinemachineVirtualCameraBase
|
|||
|
/// children are counted.</param>
|
|||
|
/// <returns>The weight of the camera. Valid only if camera is active and enabled.</returns>
|
|||
|
public float GetWeight(int index)
|
|||
|
{
|
|||
|
switch (index)
|
|||
|
{
|
|||
|
case 0: return m_Weight0;
|
|||
|
case 1: return m_Weight1;
|
|||
|
case 2: return m_Weight2;
|
|||
|
case 3: return m_Weight3;
|
|||
|
case 4: return m_Weight4;
|
|||
|
case 5: return m_Weight5;
|
|||
|
case 6: return m_Weight6;
|
|||
|
case 7: return m_Weight7;
|
|||
|
}
|
|||
|
Debug.LogError("CinemachineMixingCamera: Invalid index: " + index);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Set the weight of the child at an index.</summary>
|
|||
|
/// <param name="index">The child index. Only immediate CinemachineVirtualCameraBase
|
|||
|
/// children are counted.</param>
|
|||
|
/// <param name="w">The weight to set. Can be any non-negative number.</param>
|
|||
|
public void SetWeight(int index, float w)
|
|||
|
{
|
|||
|
switch (index)
|
|||
|
{
|
|||
|
case 0: m_Weight0 = w; return;
|
|||
|
case 1: m_Weight1 = w; return;
|
|||
|
case 2: m_Weight2 = w; return;
|
|||
|
case 3: m_Weight3 = w; return;
|
|||
|
case 4: m_Weight4 = w; return;
|
|||
|
case 5: m_Weight5 = w; return;
|
|||
|
case 6: m_Weight6 = w; return;
|
|||
|
case 7: m_Weight7 = w; return;
|
|||
|
}
|
|||
|
Debug.LogError("CinemachineMixingCamera: Invalid index: " + index);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Get the weight of the child CinemachineVirtualCameraBase.</summary>
|
|||
|
/// <param name="vcam">The child camera.</param>
|
|||
|
/// <returns>The weight of the camera. Valid only if camera is active and enabled.</returns>
|
|||
|
public float GetWeight(CinemachineVirtualCameraBase vcam)
|
|||
|
{
|
|||
|
ValidateListOfChildren();
|
|||
|
int index;
|
|||
|
if (m_indexMap.TryGetValue(vcam, out index))
|
|||
|
return GetWeight(index);
|
|||
|
Debug.LogError("CinemachineMixingCamera: Invalid child: "
|
|||
|
+ ((vcam != null) ? vcam.Name : "(null)"));
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Set the weight of the child CinemachineVirtualCameraBase.</summary>
|
|||
|
/// <param name="vcam">The child camera.</param>
|
|||
|
/// <param name="w">The weight to set. Can be any non-negative number.</param>
|
|||
|
public void SetWeight(CinemachineVirtualCameraBase vcam, float w)
|
|||
|
{
|
|||
|
ValidateListOfChildren();
|
|||
|
int index;
|
|||
|
if (m_indexMap.TryGetValue(vcam, out index))
|
|||
|
SetWeight(index, w);
|
|||
|
else
|
|||
|
Debug.LogError("CinemachineMixingCamera: Invalid child: "
|
|||
|
+ ((vcam != null) ? vcam.Name : "(null)"));
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Blended camera state</summary>
|
|||
|
private CameraState m_State = CameraState.Default;
|
|||
|
|
|||
|
/// <summary>Get the current "best" child virtual camera, which is nominally
|
|||
|
/// the one with the greatest weight.</summary>
|
|||
|
private ICinemachineCamera LiveChild { get; set; }
|
|||
|
|
|||
|
/// <summary>The blended CameraState</summary>
|
|||
|
public override CameraState State { get { return m_State; } }
|
|||
|
|
|||
|
/// <summary>Not used</summary>
|
|||
|
override public Transform LookAt { get; set; }
|
|||
|
|
|||
|
/// <summary>Not used</summary>
|
|||
|
override public Transform Follow { get; set; }
|
|||
|
|
|||
|
/// <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)
|
|||
|
{
|
|||
|
ValidateListOfChildren();
|
|||
|
foreach (var vcam in m_ChildCameras)
|
|||
|
vcam.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)
|
|||
|
{
|
|||
|
ValidateListOfChildren();
|
|||
|
foreach (var vcam in m_ChildCameras)
|
|||
|
vcam.ForceCameraPosition(pos, rot);
|
|||
|
base.ForceCameraPosition(pos, rot);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Makes sure the internal child cache is up to date</summary>
|
|||
|
protected override void OnEnable()
|
|||
|
{
|
|||
|
base.OnEnable();
|
|||
|
InvalidateListOfChildren();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Makes sure the internal child cache is up to date</summary>
|
|||
|
public void OnTransformChildrenChanged()
|
|||
|
{
|
|||
|
InvalidateListOfChildren();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Makes sure the weights are non-negative</summary>
|
|||
|
protected override void OnValidate()
|
|||
|
{
|
|||
|
base.OnValidate();
|
|||
|
for (int i = 0; i < MaxCameras; ++i)
|
|||
|
SetWeight(i, Mathf.Max(0, GetWeight(i)));
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Check whether the vcam a live child of this camera.</summary>
|
|||
|
/// <param name="vcam">The Virtual Camera to check</param>
|
|||
|
/// <param name="dominantChildOnly">If truw, will only return true if this vcam is the dominat live child</param>
|
|||
|
/// <returns>True if the vcam is currently actively influencing the state of this vcam</returns>
|
|||
|
public override bool IsLiveChild(ICinemachineCamera vcam, bool dominantChildOnly = false)
|
|||
|
{
|
|||
|
CinemachineVirtualCameraBase[] children = ChildCameras;
|
|||
|
for (int i = 0; i < MaxCameras && i < children.Length; ++i)
|
|||
|
if ((ICinemachineCamera)children[i] == vcam)
|
|||
|
return GetWeight(i) > UnityVectorExtensions.Epsilon && children[i].isActiveAndEnabled;
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
private CinemachineVirtualCameraBase[] m_ChildCameras;
|
|||
|
private Dictionary<CinemachineVirtualCameraBase, int> m_indexMap;
|
|||
|
|
|||
|
/// <summary>Get the cached list of child cameras.
|
|||
|
/// These are just the immediate children in the hierarchy.
|
|||
|
/// Note: only the first entries of this list participate in the
|
|||
|
/// final blend, up to MaxCameras</summary>
|
|||
|
public CinemachineVirtualCameraBase[] ChildCameras
|
|||
|
{
|
|||
|
get { ValidateListOfChildren(); return m_ChildCameras; }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Invalidate the cached list of child cameras.</summary>
|
|||
|
protected void InvalidateListOfChildren()
|
|||
|
{
|
|||
|
m_ChildCameras = null;
|
|||
|
m_indexMap = null;
|
|||
|
LiveChild = null;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Rebuild the cached list of child cameras.</summary>
|
|||
|
protected void ValidateListOfChildren()
|
|||
|
{
|
|||
|
if (m_ChildCameras != null)
|
|||
|
return;
|
|||
|
|
|||
|
m_indexMap = new Dictionary<CinemachineVirtualCameraBase, int>();
|
|||
|
List<CinemachineVirtualCameraBase> list = new List<CinemachineVirtualCameraBase>();
|
|||
|
CinemachineVirtualCameraBase[] kids
|
|||
|
= GetComponentsInChildren<CinemachineVirtualCameraBase>(true);
|
|||
|
foreach (CinemachineVirtualCameraBase k in kids)
|
|||
|
{
|
|||
|
if (k.transform.parent == transform)
|
|||
|
{
|
|||
|
int index = list.Count;
|
|||
|
list.Add(k);
|
|||
|
if (index < MaxCameras)
|
|||
|
m_indexMap.Add(k, index);
|
|||
|
}
|
|||
|
}
|
|||
|
m_ChildCameras = list.ToArray();
|
|||
|
}
|
|||
|
|
|||
|
/// <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);
|
|||
|
InvokeOnTransitionInExtensions(fromCam, worldUp, deltaTime);
|
|||
|
CinemachineVirtualCameraBase[] children = ChildCameras;
|
|||
|
for (int i = 0; i < MaxCameras && i < children.Length; ++i)
|
|||
|
{
|
|||
|
CinemachineVirtualCameraBase vcam = children[i];
|
|||
|
if (vcam.isActiveAndEnabled && GetWeight(i) > UnityVectorExtensions.Epsilon)
|
|||
|
vcam.OnTransitionFromCamera(fromCam, worldUp, deltaTime);
|
|||
|
}
|
|||
|
InternalUpdateCameraState(worldUp, deltaTime);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Internal use only. Do not call this methid.
|
|||
|
/// Called by CinemachineCore at designated update time
|
|||
|
/// so the vcam can position itself and track its targets. This implementation
|
|||
|
/// computes and caches the weighted blend of the tracked cameras.</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>
|
|||
|
public override void InternalUpdateCameraState(Vector3 worldUp, float deltaTime)
|
|||
|
{
|
|||
|
CinemachineVirtualCameraBase[] children = ChildCameras;
|
|||
|
LiveChild = null;
|
|||
|
float highestWeight = 0;
|
|||
|
float totalWeight = 0;
|
|||
|
for (int i = 0; i < MaxCameras && i < children.Length; ++i)
|
|||
|
{
|
|||
|
CinemachineVirtualCameraBase vcam = children[i];
|
|||
|
if (vcam.isActiveAndEnabled)
|
|||
|
{
|
|||
|
float weight = Mathf.Max(0, GetWeight(i));
|
|||
|
if (weight > UnityVectorExtensions.Epsilon)
|
|||
|
{
|
|||
|
totalWeight += weight;
|
|||
|
if (totalWeight == weight)
|
|||
|
m_State = vcam.State;
|
|||
|
else
|
|||
|
m_State = CameraState.Lerp(m_State, vcam.State, weight / totalWeight);
|
|||
|
|
|||
|
if (weight > highestWeight)
|
|||
|
{
|
|||
|
highestWeight = weight;
|
|||
|
LiveChild = vcam;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
InvokePostPipelineStageCallback(
|
|||
|
this, CinemachineCore.Stage.Finalize, ref m_State, deltaTime);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|