using System.Collections.Generic; using UnityEngine; namespace Cinemachine { /// /// Base class for a Cinemachine Virtual Camera extension module. /// Hooks into the Cinemachine Pipeline. Use this to add extra processing /// to the vcam, modifying its generated state /// [DocumentationSorting(DocumentationSortingAttribute.Level.API)] public abstract class CinemachineExtension : MonoBehaviour { /// Useful constant for very small floats protected const float Epsilon = Utility.UnityVectorExtensions.Epsilon; /// Get the CinemachineVirtualCamera to which this extension is attached public CinemachineVirtualCameraBase VirtualCamera { get { if (m_vcamOwner == null) m_vcamOwner = GetComponent(); return m_vcamOwner; } } CinemachineVirtualCameraBase m_vcamOwner; /// Connect to virtual camera pipeline. /// Override implementations must call this base implementation protected virtual void Awake() { ConnectToVcam(true); } /// Does nothing. It's here for the little checkbox in the inspector. protected virtual void OnEnable() {} #if UNITY_EDITOR [UnityEditor.Callbacks.DidReloadScripts] static void OnScriptReload() { var extensions = Resources.FindObjectsOfTypeAll(); // Sort by execution order System.Array.Sort(extensions, (x, y) => UnityEditor.MonoImporter.GetExecutionOrder(UnityEditor.MonoScript.FromMonoBehaviour(x)) - UnityEditor.MonoImporter.GetExecutionOrder(UnityEditor.MonoScript.FromMonoBehaviour(y))); foreach (var e in extensions) e.ConnectToVcam(true); } #endif /// Disconnect from virtual camera pipeline. /// Override implementations must call this base implementation protected virtual void OnDestroy() { ConnectToVcam(false); } internal void EnsureStarted() { ConnectToVcam(true); } /// Connect to virtual camera. Implementation must be safe to be called /// redundantly. Override implementations must call this base implementation /// True if connecting, false if disconnecting protected virtual void ConnectToVcam(bool connect) { if (connect && VirtualCamera == null) Debug.LogError("CinemachineExtension requires a Cinemachine Virtual Camera component"); if (VirtualCamera != null) { if (connect) VirtualCamera.AddExtension(this); else VirtualCamera.RemoveExtension(this); } mExtraState = null; } /// Override this to do such things as offset the RefereceLookAt. /// Base class implementation does nothing. /// The virtual camera being processed /// Input state that must be mutated /// The current applicable deltaTime public virtual void PrePipelineMutateCameraStateCallback( CinemachineVirtualCameraBase vcam, ref CameraState curState, float deltaTime) {} /// Legacy support. This is only here to avoid changing the API /// to make PostPipelineStageCallback() public /// The virtual camera being processed /// The current pipeline stage /// The current virtual camera state /// The current applicable deltaTime public void InvokePostPipelineStageCallback( CinemachineVirtualCameraBase vcam, CinemachineCore.Stage stage, ref CameraState state, float deltaTime) { PostPipelineStageCallback(vcam, stage, ref state, deltaTime); } /// /// This callback will be called after the virtual camera has implemented /// each stage in the pipeline. This method may modify the referenced state. /// If deltaTime less than 0, reset all state info and perform no damping. /// /// The virtual camera being processed /// The current pipeline stage /// The current virtual camera state /// The current applicable deltaTime protected abstract void PostPipelineStageCallback( CinemachineVirtualCameraBase vcam, CinemachineCore.Stage stage, ref CameraState state, float deltaTime); /// This is called to notify the extension that a target got warped, /// so that the extension can update its internal state to make the camera /// also warp seamlessy. Base class implementation does nothing. /// The object that was warped /// The amount the target's position changed public virtual void OnTargetObjectWarped(Transform target, Vector3 positionDelta) {} /// /// Force the virtual camera to assume a given position and orientation /// /// Worldspace pposition to take /// Worldspace orientation to take public virtual void ForceCameraPosition(Vector3 pos, Quaternion rot) {} /// Notification that this virtual camera is going live. /// Base class implementation must 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) /// True to request a vcam update of internal state public virtual bool OnTransitionFromCamera( ICinemachineCamera fromCam, Vector3 worldUp, float deltaTime) { return false; } /// /// Report maximum damping time needed for this extension. /// Only used in editor for timeline scrubbing. /// /// Highest damping setting in this extension public virtual float GetMaxDampTime() { return 0; } /// Extensions that require user input should implement this and return true. public virtual bool RequiresUserInput => false; /// Because extensions can be placed on manager cams and will in that /// case be called for all the vcam children, vcam-specific state information /// should be stored here. Just define a class to hold your state info /// and use it exclusively when calling this. /// /// The type of the extra state class /// The virtual camera being processed /// The extra state, cast as type T protected T GetExtraState(ICinemachineCamera vcam) where T : class, new() { if (mExtraState == null) mExtraState = new Dictionary(); System.Object extra = null; if (!mExtraState.TryGetValue(vcam, out extra)) extra = mExtraState[vcam] = new T(); return extra as T; } /// Inefficient method to get all extra state info for all vcams. /// Intended for Editor use only, not runtime! /// /// The extra state type /// A dynamically-allocated list with all the extra states protected List GetAllExtraStates() where T : class, new() { var list = new List(); if (mExtraState != null) foreach (var v in mExtraState) list.Add(v.Value as T); return list; } private Dictionary mExtraState; } }