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; } }