using UnityEngine;
using Cinemachine.Utility;
using System.Collections.Generic;
namespace Cinemachine
{
///
/// 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.
///
[DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)]
[DisallowMultipleComponent]
[ExecuteAlways]
[ExcludeFromPreset]
[AddComponentMenu("Cinemachine/CinemachineMixingCamera")]
[HelpURL(Documentation.BaseURL + "manual/CinemachineMixingCamera.html")]
public class CinemachineMixingCamera : CinemachineVirtualCameraBase
{
/// 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.
public const int MaxCameras = 8;
/// Weight of the first tracked camera
[Tooltip("The weight of the first tracked camera")]
public float m_Weight0 = 0.5f;
/// Weight of the second tracked camera
[Tooltip("The weight of the second tracked camera")]
public float m_Weight1 = 0.5f;
/// Weight of the third tracked camera
[Tooltip("The weight of the third tracked camera")]
public float m_Weight2 = 0.5f;
/// Weight of the fourth tracked camera
[Tooltip("The weight of the fourth tracked camera")]
public float m_Weight3 = 0.5f;
/// Weight of the fifth tracked camera
[Tooltip("The weight of the fifth tracked camera")]
public float m_Weight4 = 0.5f;
/// Weight of the sixth tracked camera
[Tooltip("The weight of the sixth tracked camera")]
public float m_Weight5 = 0.5f;
/// Weight of the seventh tracked camera
[Tooltip("The weight of the seventh tracked camera")]
public float m_Weight6 = 0.5f;
/// Weight of the eighth tracked camera
[Tooltip("The weight of the eighth tracked camera")]
public float m_Weight7 = 0.5f;
/// Get the weight of the child at an index.
/// The child index. Only immediate CinemachineVirtualCameraBase
/// children are counted.
/// The weight of the camera. Valid only if camera is active and enabled.
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;
}
/// Set the weight of the child at an index.
/// The child index. Only immediate CinemachineVirtualCameraBase
/// children are counted.
/// The weight to set. Can be any non-negative number.
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);
}
/// Get the weight of the child CinemachineVirtualCameraBase.
/// The child camera.
/// The weight of the camera. Valid only if camera is active and enabled.
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;
}
/// Set the weight of the child CinemachineVirtualCameraBase.
/// The child camera.
/// The weight to set. Can be any non-negative number.
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)"));
}
/// Blended camera state
private CameraState m_State = CameraState.Default;
/// Get the current "best" child virtual camera, which is nominally
/// the one with the greatest weight.
private ICinemachineCamera LiveChild { get; set; }
/// The blended CameraState
public override CameraState State { get { return m_State; } }
/// Not used
override public Transform LookAt { get; set; }
/// Not used
override public Transform Follow { get; set; }
/// 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.
/// The object that was warped
/// The amount the target's position changed
public override void OnTargetObjectWarped(Transform target, Vector3 positionDelta)
{
ValidateListOfChildren();
foreach (var vcam in m_ChildCameras)
vcam.OnTargetObjectWarped(target, positionDelta);
base.OnTargetObjectWarped(target, positionDelta);
}
///
/// Force the virtual camera to assume a given position and orientation
///
/// Worldspace pposition to take
/// Worldspace orientation to take
public override void ForceCameraPosition(Vector3 pos, Quaternion rot)
{
ValidateListOfChildren();
foreach (var vcam in m_ChildCameras)
vcam.ForceCameraPosition(pos, rot);
base.ForceCameraPosition(pos, rot);
}
/// Makes sure the internal child cache is up to date
protected override void OnEnable()
{
base.OnEnable();
InvalidateListOfChildren();
}
/// Makes sure the internal child cache is up to date
public void OnTransformChildrenChanged()
{
InvalidateListOfChildren();
}
/// Makes sure the weights are non-negative
protected override void OnValidate()
{
base.OnValidate();
for (int i = 0; i < MaxCameras; ++i)
SetWeight(i, Mathf.Max(0, GetWeight(i)));
}
/// Check whether the vcam a live child of this camera.
/// The Virtual Camera to check
/// If truw, will only return true if this vcam is the dominat live child
/// True if the vcam is currently actively influencing the state of this vcam
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 m_indexMap;
/// 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
public CinemachineVirtualCameraBase[] ChildCameras
{
get { ValidateListOfChildren(); return m_ChildCameras; }
}
/// Invalidate the cached list of child cameras.
protected void InvalidateListOfChildren()
{
m_ChildCameras = null;
m_indexMap = null;
LiveChild = null;
}
/// Rebuild the cached list of child cameras.
protected void ValidateListOfChildren()
{
if (m_ChildCameras != null)
return;
m_indexMap = new Dictionary();
List list = new List();
CinemachineVirtualCameraBase[] kids
= GetComponentsInChildren(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();
}
/// Notification that this virtual camera is going live.
/// The camera being deactivated. May be null.
/// Default world Up, set by the CinemachineBrain
/// Delta time for time-based effects (ignore if less than or equal to 0)
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)
children[i].OnTransitionFromCamera(fromCam, worldUp, deltaTime);
InternalUpdateCameraState(worldUp, deltaTime);
}
/// 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.
/// Default world Up, set by the CinemachineBrain
/// Delta time for time-based effects (ignore if less than 0)
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);
}
}
}