using System; using UnityEngine; namespace Cinemachine { /// /// Base class for a Monobehaviour that represents a Virtual Camera within the Unity scene. /// /// This is intended to be attached to an empty Transform GameObject. /// Inherited classes can be either standalone virtual cameras such /// as CinemachineVirtualCamera, or meta-cameras such as /// CinemachineClearShot or CinemachineFreeLook. /// /// A CinemachineVirtualCameraBase exposes a Priority property. When the behaviour is /// enabled in the game, the Virtual Camera is automatically placed in a queue /// maintained by the static CinemachineCore singleton. /// The queue is sorted by priority. When a Unity camera is equipped with a /// CinemachineBrain behaviour, the brain will choose the camera /// at the head of the queue. If you have multiple Unity cameras with CinemachineBrain /// behaviours (say in a split-screen context), then you can filter the queue by /// setting the culling flags on the virtual cameras. The culling mask of the /// Unity Camera will then act as a filter for the brain. Apart from this, /// there is nothing that prevents a virtual camera from controlling multiple /// Unity cameras simultaneously. /// [SaveDuringPlay] public abstract class CinemachineVirtualCameraBase : MonoBehaviour, ICinemachineCamera { /// This is deprecated. It is here to support the soon-to-be-removed /// Cinemachine Debugger in the Editor. [HideInInspector, NoSaveDuringPlay] public Action CinemachineGUIDebuggerCallback = null; /// Inspector control - Use for hiding sections of the Inspector UI. [HideInInspector, SerializeField, NoSaveDuringPlay] public string[] m_ExcludedPropertiesInInspector = new string[] { "m_Script" }; /// Inspector control - Use for enabling sections of the Inspector UI. [HideInInspector, SerializeField, NoSaveDuringPlay] public CinemachineCore.Stage[] m_LockStageInInspector; /// Version that was last streamed, for upgrading legacy public int ValidatingStreamVersion { get { return m_OnValidateCalled ? m_ValidatingStreamVersion : CinemachineCore.kStreamingVersion; } private set { m_ValidatingStreamVersion = value; } } private int m_ValidatingStreamVersion = 0; private bool m_OnValidateCalled = false; [HideInInspector, SerializeField, NoSaveDuringPlay] private int m_StreamingVersion; /// The priority will determine which camera becomes active based on the /// state of other cameras and this camera. Higher numbers have greater priority. /// [NoSaveDuringPlay] [Tooltip("The priority will determine which camera becomes active based on the state of other cameras and this camera. Higher numbers have greater priority.")] public int m_Priority = 10; /// /// A delegate to hook into the state calculation pipeline. /// This will be called after each pipeline stage, to allow others to hook into the pipeline. /// See CinemachineCore.Stage. /// /// The delegate to call. public virtual void AddPostPipelineStageHook(OnPostPipelineStageDelegate d) { OnPostPipelineStage -= d; OnPostPipelineStage += d; } /// Remove a Pipeline stage hook callback. /// The delegate to remove. public virtual void RemovePostPipelineStageHook(OnPostPipelineStageDelegate d) { OnPostPipelineStage -= d; } /// /// A delegate to hook into the state calculation pipeline. /// This will be called after each pipeline stage, to allow other /// services to hook into the pipeline. /// See CinemachineCore.Stage. /// /// Parameters: /// /// * CinemachineVirtualCameraBase vcam: the virtual camera being updated /// * CinemachineCore.Stage stage: what stage in the pipeline has just been updated /// * ref CameraState newState: the current state of the vcam /// * float deltaTime: the frame timestep. Less than 0 means "don't consider the previous frame" /// public delegate void OnPostPipelineStageDelegate( CinemachineVirtualCameraBase vcam, CinemachineCore.Stage stage, ref CameraState newState, float deltaTime); /// /// A delegate to hook into the state calculation pipeline. /// Implementaion must be sure to call this after each pipeline stage, to allow /// other services to hook into the pipeline. /// See CinemachineCore.Stage. /// protected OnPostPipelineStageDelegate OnPostPipelineStage; /// /// Invokes the PostPipelineStageDelegate for this camera, and up the hierarchy for all /// parent cameras (if any). /// protected void InvokePostPipelineStageCallback( CinemachineVirtualCameraBase vcam, CinemachineCore.Stage stage, ref CameraState newState, float deltaTime) { if (OnPostPipelineStage != null) OnPostPipelineStage(vcam, stage, ref newState, deltaTime); CinemachineVirtualCameraBase parent = ParentCamera as CinemachineVirtualCameraBase; if (parent != null) parent.InvokePostPipelineStageCallback(vcam, stage, ref newState, deltaTime); } /// Get the name of the Virtual Camera. Base implementation /// returns the owner GameObject's name. public string Name { get { return name; } } /// Gets a brief debug description of this virtual camera, for use when displayiong debug info public virtual string Description { get { return ""; }} /// Get the Priority of the virtual camera. This determines its placement /// in the CinemachineCore's queue of eligible shots. public int Priority { get { return m_Priority; } set { m_Priority = value; } } /// The GameObject owner of the Virtual Camera behaviour. public GameObject VirtualCameraGameObject { get { if (this == null) return null; // object deleted return gameObject; } } /// The CameraState object holds all of the information /// necessary to position the Unity camera. It is the output of this class. public abstract CameraState State { get; } /// Just returns self. public virtual ICinemachineCamera LiveChildOrSelf { get { return this; } } /// Support for meta-virtual-cameras. This is the situation where a /// virtual camera is in fact the public face of a private army of virtual cameras, which /// it manages on its own. This method gets the VirtualCamera owner, if any. /// Private armies are implemented as Transform children of the parent vcam. public ICinemachineCamera ParentCamera { get { if (!mSlaveStatusUpdated || !Application.isPlaying) UpdateSlaveStatus(); return m_parentVcam; } } /// Check whether the vcam a live child of this camera. /// This base class implementation always returns false. /// The Virtual Camera to check /// True if the vcam is currently actively influencing the state of this vcam public virtual bool IsLiveChild(ICinemachineCamera vcam) { return false; } /// Get the LookAt target for the Aim component in the CinemachinePipeline. public abstract Transform LookAt { get; set; } /// Get the Follow target for the Body component in the CinemachinePipeline. public abstract Transform Follow { get; set; } /// Set this to force the next update to ignore deltaTime and reset itself public bool PreviousStateIsValid { get { if (LookAt != m_previousLookAtTarget) { m_previousLookAtTarget = LookAt; m_previousStateIsValid = false; } if (Follow != m_previousFollowTarget) { m_previousFollowTarget = Follow; m_previousStateIsValid = false; } return m_previousStateIsValid; } set { m_previousStateIsValid = value; } } private bool m_previousStateIsValid; private Transform m_previousLookAtTarget; private Transform m_previousFollowTarget; /// Called by CinemachineCore at designated update time /// so the vcam can position itself and track its targets. /// Do not call this method. Let the framework do it at the appropriate time /// Default world Up, set by the CinemachineBrain /// Delta time for time-based effects (ignore if less than 0) public abstract void UpdateCameraState(Vector3 worldUp, float deltaTime); /// Notification that this virtual camera is going live. /// Base class implementationmust be called by any overridden method. /// 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 virtual void OnTransitionFromCamera( ICinemachineCamera fromCam, Vector3 worldUp, float deltaTime) { if (!gameObject.activeInHierarchy) PreviousStateIsValid = false; } /// Base class implementation does nothing. protected virtual void Start() { } /// Base class implementation removes the virtual camera from the priority queue. protected virtual void OnDestroy() { CinemachineCore.Instance.RemoveActiveCamera(this); } /// Enforce bounds for fields, when changed in inspector. /// Call base class implementation at the beginning of overridden method. /// After base method is called, ValidatingStreamVersion will be valid. protected virtual void OnValidate() { m_OnValidateCalled = true; ValidatingStreamVersion = m_StreamingVersion; m_StreamingVersion = CinemachineCore.kStreamingVersion; } /// Base class implementation adds the virtual camera from the priority queue. protected virtual void OnEnable() { // Sanity check - if another vcam component is enabled, shut down var vcamComponents = GetComponents(); for (int i = 0; i < vcamComponents.Length; ++i) { if (vcamComponents[i].enabled && vcamComponents[i] != this) { Debug.LogError(Name + " has multiple CinemachineVirtualCameraBase-derived components. Disabling " + GetType().Name + "."); enabled = false; } } UpdateSlaveStatus(); UpdateVcamPoolStatus(); // Add to queue PreviousStateIsValid = false; } /// Base class implementation makes sure the priority queue remains up-to-date. protected virtual void OnDisable() { UpdateVcamPoolStatus(); // Remove from queue } /// Base class implementation makes sure the priority queue remains up-to-date. protected virtual void Update() { if (m_Priority != m_QueuePriority) UpdateVcamPoolStatus(); } /// Base class implementation makes sure the priority queue remains up-to-date. protected virtual void OnTransformParentChanged() { UpdateSlaveStatus(); UpdateVcamPoolStatus(); } #if UNITY_EDITOR /// Support for the deprecated CinemachineDebugger. protected virtual void OnGUI() { if (CinemachineGUIDebuggerCallback != null) CinemachineGUIDebuggerCallback(); } #endif private bool mSlaveStatusUpdated = false; private CinemachineVirtualCameraBase m_parentVcam = null; private void UpdateSlaveStatus() { mSlaveStatusUpdated = true; m_parentVcam = null; Transform p = transform.parent; if (p != null) m_parentVcam = p.GetComponent(); } /// Returns this vcam's LookAt target, or if that is null, will retrun /// the parent vcam's LookAt target. /// This vcam's LookAt value. /// The same value, or the parent's if null and a parent exists. protected Transform ResolveLookAt(Transform localLookAt) { Transform lookAt = localLookAt; if (lookAt == null && ParentCamera != null) lookAt = ParentCamera.LookAt; // Parent provides default return lookAt; } /// Returns this vcam's Follow target, or if that is null, will retrun /// the parent vcam's Follow target. /// This vcam's Follow value. /// The same value, or the parent's if null and a parent exists. protected Transform ResolveFollow(Transform localFollow) { Transform follow = localFollow; if (follow == null && ParentCamera != null) follow = ParentCamera.Follow; // Parent provides default return follow; } private int m_QueuePriority = int.MaxValue; private void UpdateVcamPoolStatus() { m_QueuePriority = int.MaxValue; CinemachineCore.Instance.RemoveActiveCamera(this); CinemachineCore.Instance.RemoveChildCamera(this); if (m_parentVcam == null) { if (isActiveAndEnabled) { CinemachineCore.Instance.AddActiveCamera(this); m_QueuePriority = m_Priority; } } else { if (isActiveAndEnabled) CinemachineCore.Instance.AddChildCamera(this); } } /// When multiple virtual cameras have the highest priority, there is /// sometimes the need to push one to the top, making it the current Live camera if /// it shares the highest priority in the queue with its peers. /// /// This happens automatically when a /// new vcam is enabled: the most recent one goes to the top of the priority subqueue. /// Use this method to push a vcam to the top of its priority peers. /// If it and its peers share the highest priority, then this vcam will become Live. public void MoveToTopOfPrioritySubqueue() { UpdateVcamPoolStatus(); } } }