using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
namespace Unity.Cinemachine
{
///
/// This is a virtual camera "manager" that owns and manages a collection
/// of child Virtual Cameras. When the camera goes live, these child vcams
/// are enabled, one after another, holding each camera for a designated time.
/// Blends between cameras are specified.
/// The last camera is held indefinitely, unless the Loop flag is enabled.
///
[DisallowMultipleComponent]
[ExecuteAlways]
[ExcludeFromPreset]
[AddComponentMenu("Cinemachine/Cinemachine Sequencer Camera")]
[HelpURL(Documentation.BaseURL + "manual/CinemachineSequencerCamera.html")]
public class CinemachineSequencerCamera : CinemachineCameraManagerBase
{
/// When enabled, the child vcams will cycle indefinitely instead of just stopping at the last one
[Tooltip("When enabled, the child vcams will cycle indefinitely instead of just stopping at the last one")]
[FormerlySerializedAs("m_Loop")]
public bool Loop;
/// This represents a single entry in the instruction list of the BlendListCamera.
[Serializable]
public struct Instruction
{
/// The camera to activate when this instruction becomes active
[Tooltip("The camera to activate when this instruction becomes active")]
[FormerlySerializedAs("m_VirtualCamera")]
[ChildCameraProperty]
public CinemachineVirtualCameraBase Camera;
/// How to blend to the next camera in the list (if any)
[Tooltip("How to blend to the next camera in the list (if any)")]
[FormerlySerializedAs("m_Blend")]
public CinemachineBlendDefinition Blend;
/// How long to wait (in seconds) before activating the next camera in the list (if any)
[Tooltip("How long to wait (in seconds) before activating the next camera in the list (if any)")]
[FormerlySerializedAs("m_Hold")]
public float Hold;
/// Clamp the the settings to sensible valuse.
public void Validate() => Hold = Mathf.Max(Hold, 0);
};
/// The set of instructions for enabling child cameras
[Tooltip("The set of instructions for enabling child cameras.")]
[FormerlySerializedAs("m_Instructions")]
public List Instructions = new ();
[SerializeField, HideInInspector, FormerlySerializedAs("m_LookAt")] Transform m_LegacyLookAt;
[SerializeField, HideInInspector, FormerlySerializedAs("m_Follow")] Transform m_LegacyFollow;
float m_ActivationTime = -1; // The time at which the current instruction went live
int m_CurrentInstruction = 0;
///
protected override void Reset()
{
base.Reset();
Loop = false;
Instructions = null;
}
void OnValidate()
{
if (Instructions != null)
{
for (int i = 0; i < Instructions.Count; ++i)
{
var e = Instructions[i];
e.Validate();
Instructions[i] = e;
}
}
}
protected internal override void PerformLegacyUpgrade(int streamedVersion)
{
base.PerformLegacyUpgrade(streamedVersion);
if (streamedVersion < 20220721)
{
if (m_LegacyLookAt != null || m_LegacyFollow != null)
{
DefaultTarget = new DefaultTargetSettings
{
Enabled = true,
Target = new CameraTarget
{
LookAtTarget = m_LegacyLookAt,
TrackingTarget = m_LegacyFollow,
CustomLookAtTarget = m_LegacyLookAt != m_LegacyFollow
}
};
m_LegacyLookAt = m_LegacyFollow = null;
}
}
}
///
public override void OnTransitionFromCamera(
ICinemachineCamera fromCam, Vector3 worldUp, float deltaTime)
{
m_ActivationTime = CinemachineCore.CurrentTime;
m_CurrentInstruction = 0;
}
///
protected override CinemachineVirtualCameraBase ChooseCurrentCamera(Vector3 worldUp, float deltaTime)
{
if (!PreviousStateIsValid)
{
m_CurrentInstruction = -1;
ValidateInstructions();
}
AdvanceCurrentInstruction(deltaTime);
return (m_CurrentInstruction >= 0 && m_CurrentInstruction < Instructions.Count)
? Instructions[m_CurrentInstruction].Camera : null;
}
/// Returns the current blend of the current instruction.
/// The camera we're blending from (ignored).
/// The camera we're blending to (ignored).
/// The blend to use for this camera transition.
protected override CinemachineBlendDefinition LookupBlend(
ICinemachineCamera outgoing, ICinemachineCamera incoming) => Instructions[m_CurrentInstruction].Blend;
/// Internal API for the inspector editor.
/// // GML todo: make this private, part of UpdateCameraCache()
internal void ValidateInstructions()
{
Instructions ??= new ();
for (var i = 0; i < Instructions.Count; ++i)
{
if (Instructions[i].Camera != null
&& Instructions[i].Camera.transform.parent != transform)
{
var e = Instructions[i];
e.Camera = null;
Instructions[i] = e;
}
}
}
void AdvanceCurrentInstruction(float deltaTime)
{
if (ChildCameras == null || ChildCameras.Count == 0
|| m_ActivationTime < 0 || Instructions.Count == 0)
{
m_ActivationTime = -1;
m_CurrentInstruction = -1;
return;
}
var now = CinemachineCore.CurrentTime;
if (m_CurrentInstruction < 0 || deltaTime < 0)
{
m_ActivationTime = now;
m_CurrentInstruction = 0;
}
if (m_CurrentInstruction > Instructions.Count - 1)
{
m_ActivationTime = now;
m_CurrentInstruction = Instructions.Count - 1;
}
var holdTime = Instructions[m_CurrentInstruction].Hold
+ Instructions[m_CurrentInstruction].Blend.BlendTime;
var minHold = m_CurrentInstruction < Instructions.Count - 1 || Loop ? 0 : float.MaxValue;
if (now - m_ActivationTime > Mathf.Max(minHold, holdTime))
{
m_ActivationTime = now;
++m_CurrentInstruction;
if (Loop && m_CurrentInstruction == Instructions.Count)
m_CurrentInstruction = 0;
}
}
}
}