using UnityEngine;
using System.Collections.Generic;
using UnityEngine.Serialization;
namespace Unity.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.
///
[DisallowMultipleComponent]
[ExecuteAlways]
[ExcludeFromPreset]
[AddComponentMenu("Cinemachine/Cinemachine Mixing Camera")]
[HelpURL(Documentation.BaseURL + "manual/CinemachineMixingCamera.html")]
public class CinemachineMixingCamera : CinemachineCameraManagerBase
{
/// 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")]
[FormerlySerializedAs("m_Weight0")]
public float Weight0 = 0.5f;
/// Weight of the second tracked camera
[Tooltip("The weight of the second tracked camera")]
[FormerlySerializedAs("m_Weight1")]
public float Weight1 = 0.5f;
/// Weight of the third tracked camera
[Tooltip("The weight of the third tracked camera")]
[FormerlySerializedAs("m_Weight2")]
public float Weight2 = 0.5f;
/// Weight of the fourth tracked camera
[Tooltip("The weight of the fourth tracked camera")]
[FormerlySerializedAs("m_Weight3")]
public float Weight3 = 0.5f;
/// Weight of the fifth tracked camera
[Tooltip("The weight of the fifth tracked camera")]
[FormerlySerializedAs("m_Weight4")]
public float Weight4 = 0.5f;
/// Weight of the sixth tracked camera
[Tooltip("The weight of the sixth tracked camera")]
[FormerlySerializedAs("m_Weight5")]
public float Weight5 = 0.5f;
/// Weight of the seventh tracked camera
[Tooltip("The weight of the seventh tracked camera")]
[FormerlySerializedAs("m_Weight6")]
public float Weight6 = 0.5f;
/// Weight of the eighth tracked camera
[Tooltip("The weight of the eighth tracked camera")]
[FormerlySerializedAs("m_Weight7")]
public float Weight7 = 0.5f;
CameraState m_CameraState = CameraState.Default;
Dictionary m_IndexMap;
float m_LiveChildPercent;
/// Makes sure the weights are non-negative
void OnValidate()
{
for (int i = 0; i < MaxCameras; ++i)
SetWeight(i, Mathf.Max(0, GetWeight(i)));
}
///
protected override void Reset()
{
base.Reset();
for (var i = 0; i < MaxCameras; ++i)
SetWeight(i, i == 0 ? 1 : 0);
}
///
public override CameraState State => m_CameraState;
///
public override string Description
{
get
{
if (LiveChild == null)
return "[(none)]";
var sb = CinemachineDebug.SBFromPool();
sb.Append("[");
sb.Append(LiveChild.Name);
sb.Append(" ");
sb.Append(Mathf.RoundToInt(m_LiveChildPercent));
sb.Append("%]");
var text = sb.ToString();
CinemachineDebug.ReturnToPool(sb);
return text;
}
}
/// 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 Weight0;
case 1: return Weight1;
case 2: return Weight2;
case 3: return Weight3;
case 4: return Weight4;
case 5: return Weight5;
case 6: return Weight6;
case 7: return 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: Weight0 = w; return;
case 1: Weight1 = w; return;
case 2: Weight2 = w; return;
case 3: Weight3 = w; return;
case 4: Weight4 = w; return;
case 5: Weight5 = w; return;
case 6: Weight6 = w; return;
case 7: 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)
{
UpdateCameraCache();
if (m_IndexMap.TryGetValue(vcam, out var index))
return GetWeight(index);
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)
{
UpdateCameraCache();
if (m_IndexMap.TryGetValue(vcam, out var index))
SetWeight(index, w);
else
Debug.LogError("CinemachineMixingCamera: Invalid child: "
+ ((vcam != null) ? vcam.Name : "(null)"));
}
///
public override bool IsLiveChild(ICinemachineCamera vcam, bool dominantChildOnly = false)
{
if (dominantChildOnly)
return LiveChild == vcam;
var children = ChildCameras;
for (int i = 0; i < MaxCameras && i < children.Count; ++i)
if ((ICinemachineCamera)children[i] == vcam)
return GetWeight(i) > UnityVectorExtensions.Epsilon && children[i].isActiveAndEnabled;
return false;
}
///
protected override bool UpdateCameraCache()
{
if (!base.UpdateCameraCache())
return false;
m_IndexMap = new Dictionary();
for (var i = 0; i < ChildCameras.Count; ++i)
m_IndexMap.Add(ChildCameras[i], i);
return true;
}
///
public override void OnTransitionFromCamera(
ICinemachineCamera fromCam, Vector3 worldUp, float deltaTime)
{
for (int i = 0; i < MaxCameras && i < ChildCameras.Count; ++i)
ChildCameras[i].OnTransitionFromCamera(fromCam, worldUp, deltaTime);
base.OnTransitionFromCamera(fromCam, worldUp, deltaTime);
}
///
public override void InternalUpdateCameraState(Vector3 worldUp, float deltaTime)
{
UpdateCameraCache();
CinemachineVirtualCameraBase liveChild = null;
var children = ChildCameras;
float highestWeight = 0;
float totalWeight = 0;
for (var i = 0; i < MaxCameras && i < children.Count; ++i)
{
var vcam = children[i];
if (vcam.isActiveAndEnabled)
{
float weight = Mathf.Max(0, GetWeight(i));
if (weight > UnityVectorExtensions.Epsilon)
{
totalWeight += weight;
if (totalWeight == weight)
m_CameraState = vcam.State;
else
m_CameraState = CameraState.Lerp(m_CameraState, vcam.State, weight / totalWeight);
if (weight > highestWeight)
{
highestWeight = weight;
liveChild = vcam;
}
}
}
}
m_LiveChildPercent = totalWeight > 0.001f ? (highestWeight * 100 / totalWeight) : 0;
SetLiveChild(liveChild, worldUp, deltaTime);
InvokePostPipelineStageCallback(this, CinemachineCore.Stage.Finalize, ref m_CameraState, deltaTime);
PreviousStateIsValid = true;
}
///
protected override CinemachineVirtualCameraBase ChooseCurrentCamera(Vector3 worldUp, float deltaTime) => null;
}
}