| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395 |
- #if !UNITY_2019_1_OR_NEWER
- #define CINEMACHINE_UGUI
- #endif
- using UnityEngine;
- #if CINEMACHINE_UGUI
- using System.Collections.Generic;
- namespace Cinemachine
- {
- /// <summary>
- /// An add-on module for Cinemachine Virtual Camera that places an image in screen space
- /// over the camera's output.
- /// </summary>
- [SaveDuringPlay]
- [DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)]
- [AddComponentMenu("")] // Hide in menu
- #if UNITY_2018_3_OR_NEWER
- [ExecuteAlways]
- #else
- [ExecuteInEditMode]
- #endif
- [DisallowMultipleComponent]
- [HelpURL(Documentation.BaseURL + "manual/CinemachineStoryboard.html")]
- public class CinemachineStoryboard : CinemachineExtension
- {
- /// <summary>
- /// If checked, all storyboards are globally muted
- /// </summary>
- [Tooltip("If checked, all storyboards are globally muted")]
- public static bool s_StoryboardGlobalMute;
- /// <summary>
- /// If checked, the specified image will be displayed as an overlay over the virtual camera's output
- /// </summary>
- [Tooltip("If checked, the specified image will be displayed as an overlay over the virtual camera's output")]
- public bool m_ShowImage = true;
- /// <summary>
- /// The image to display
- /// </summary>
- [Tooltip("The image to display")]
- public Texture m_Image;
- /// <summary>How to fit the image in the frame, in the event that the aspect ratios don't match</summary>
- public enum FillStrategy
- {
- /// <summary>Image will be as large as possible on the screen, without being cropped</summary>
- BestFit,
- /// <summary>Image will be cropped if necessary so that the screen is entirely filled</summary>
- CropImageToFit,
- /// <summary>Image will be stretched to cover any aspect mismatch with the screen</summary>
- StretchToFit
- };
- /// <summary>
- /// How to handle differences between image aspect and screen aspect
- /// </summary>
- [Tooltip("How to handle differences between image aspect and screen aspect")]
- public FillStrategy m_Aspect = FillStrategy.BestFit;
- /// <summary>
- /// The opacity of the image. 0 is transparent, 1 is opaque
- /// </summary>
- [Tooltip("The opacity of the image. 0 is transparent, 1 is opaque")]
- [Range(0, 1)]
- public float m_Alpha = 1;
- /// <summary>
- /// The screen-space position at which to display the image. Zero is center
- /// </summary>
- [Tooltip("The screen-space position at which to display the image. Zero is center")]
- public Vector2 m_Center = Vector2.zero;
- /// <summary>
- /// The screen-space rotation to apply to the image
- /// </summary>
- [Tooltip("The screen-space rotation to apply to the image")]
- public Vector3 m_Rotation = Vector3.zero;
- /// <summary>
- /// The screen-space scaling to apply to the image
- /// </summary>
- [Tooltip("The screen-space scaling to apply to the image")]
- public Vector2 m_Scale = Vector3.one;
- /// <summary>
- /// If checked, X and Y scale are synchronized
- /// </summary>
- [Tooltip("If checked, X and Y scale are synchronized")]
- public bool m_SyncScale = true;
- /// <summary>
- /// If checked, Camera transform will not be controlled by this virtual camera
- /// </summary>
- [Tooltip("If checked, Camera transform will not be controlled by this virtual camera")]
- public bool m_MuteCamera;
- /// <summary>
- /// Wipe the image on and off horizontally
- /// </summary>
- [Range(-1, 1)]
- [Tooltip("Wipe the image on and off horizontally")]
- public float m_SplitView = 0f;
- class CanvasInfo
- {
- public GameObject mCanvas;
- public CinemachineBrain mCanvasParent;
- public RectTransform mViewport; // for mViewport clipping
- public UnityEngine.UI.RawImage mRawImage;
- }
- List<CanvasInfo> mCanvasInfo = new List<CanvasInfo>();
- /// <summary>Callback to display the image</summary>
- /// <param name="vcam">The virtual camera being processed</param>
- /// <param name="stage">The current pipeline stage</param>
- /// <param name="state">The current virtual camera state</param>
- /// <param name="deltaTime">The current applicable deltaTime</param>
- protected override void PostPipelineStageCallback(
- CinemachineVirtualCameraBase vcam,
- CinemachineCore.Stage stage, ref CameraState state, float deltaTime)
- {
- // Apply to this vcam only, not the children
- if (vcam != VirtualCamera || stage != CinemachineCore.Stage.Finalize)
- return;
- if (m_ShowImage)
- state.AddCustomBlendable(new CameraState.CustomBlendable(this, 1));
- if (m_MuteCamera)
- state.BlendHint |= CameraState.BlendHintValue.NoTransform | CameraState.BlendHintValue.NoLens;
- }
- /// <summary>Connect to virtual camera. Adds/removes listener</summary>
- /// <param name="connect">True if connecting, false if disconnecting</param>
- protected override void ConnectToVcam(bool connect)
- {
- base.ConnectToVcam(connect);
- CinemachineCore.CameraUpdatedEvent.RemoveListener(CameraUpdatedCallback);
- if (connect)
- CinemachineCore.CameraUpdatedEvent.AddListener(CameraUpdatedCallback);
- else
- DestroyCanvas();
- }
- string CanvasName { get { return "_CM_canvas" + gameObject.GetInstanceID().ToString(); } }
- void CameraUpdatedCallback(CinemachineBrain brain)
- {
- bool showIt = enabled && m_ShowImage && CinemachineCore.Instance.IsLive(VirtualCamera);
- int layer = 1 << gameObject.layer;
- if (brain.OutputCamera == null || (brain.OutputCamera.cullingMask & layer) == 0)
- showIt = false;
- if (s_StoryboardGlobalMute)
- showIt = false;
- CanvasInfo ci = LocateMyCanvas(brain, showIt);
- if (ci != null && ci.mCanvas != null)
- ci.mCanvas.SetActive(showIt);
- }
- CanvasInfo LocateMyCanvas(CinemachineBrain parent, bool createIfNotFound)
- {
- CanvasInfo ci = null;
- for (int i = 0; ci == null && i < mCanvasInfo.Count; ++i)
- if (mCanvasInfo[i].mCanvasParent == parent)
- ci = mCanvasInfo[i];
- if (createIfNotFound)
- {
- if (ci == null)
- {
- ci = new CanvasInfo() { mCanvasParent = parent };
- int numChildren = parent.transform.childCount;
- for (int i = 0; ci.mCanvas == null && i < numChildren; ++i)
- {
- RectTransform child = parent.transform.GetChild(i) as RectTransform;
- if (child != null && child.name == CanvasName)
- {
- ci.mCanvas = child.gameObject;
- ci.mViewport = ci.mCanvas.GetComponentsInChildren<RectTransform>()[1]; // 0 is mCanvas
- ci.mRawImage = ci.mCanvas.GetComponentInChildren<UnityEngine.UI.RawImage>();
- }
- }
- mCanvasInfo.Add(ci);
- }
- if (ci.mCanvas == null || ci.mViewport == null || ci.mRawImage == null)
- CreateCanvas(ci);
- }
- return ci;
- }
- void CreateCanvas(CanvasInfo ci)
- {
- ci.mCanvas = new GameObject(CanvasName, typeof(RectTransform));
- ci.mCanvas.layer = gameObject.layer;
- ci.mCanvas.hideFlags = HideFlags.HideAndDontSave;
- ci.mCanvas.transform.SetParent(ci.mCanvasParent.transform);
- #if UNITY_EDITOR
- // Workaround for Unity bug case Case 1004117
- CanvasesAndTheirOwners.AddCanvas(ci.mCanvas, this);
- #endif
- var c = ci.mCanvas.AddComponent<Canvas>();
- c.renderMode = RenderMode.ScreenSpaceOverlay;
- var go = new GameObject("Viewport", typeof(RectTransform));
- go.transform.SetParent(ci.mCanvas.transform);
- ci.mViewport = (RectTransform)go.transform;
- go.AddComponent<UnityEngine.UI.RectMask2D>();
- go = new GameObject("RawImage", typeof(RectTransform));
- go.transform.SetParent(ci.mViewport.transform);
- ci.mRawImage = go.AddComponent<UnityEngine.UI.RawImage>();
- }
- void DestroyCanvas()
- {
- int numBrains = CinemachineCore.Instance.BrainCount;
- for (int i = 0; i < numBrains; ++i)
- {
- var parent = CinemachineCore.Instance.GetActiveBrain(i);
- int numChildren = parent.transform.childCount;
- for (int j = numChildren - 1; j >= 0; --j)
- {
- RectTransform child = parent.transform.GetChild(j) as RectTransform;
- if (child != null && child.name == CanvasName)
- {
- var canvas = child.gameObject;
- RuntimeUtility.DestroyObject(canvas);
- #if UNITY_EDITOR
- // Workaround for Unity bug case Case 1004117
- CanvasesAndTheirOwners.RemoveCanvas(canvas);
- #endif
- }
- }
- }
- mCanvasInfo.Clear();
- }
- void PlaceImage(CanvasInfo ci, float alpha)
- {
- if (ci.mRawImage != null && ci.mViewport != null)
- {
- Rect screen = new Rect(0, 0, Screen.width, Screen.height);
- if (ci.mCanvasParent.OutputCamera != null)
- screen = ci.mCanvasParent.OutputCamera.pixelRect;
- screen.x -= (float)Screen.width/2;
- screen.y -= (float)Screen.height/2;
- // Apply Split View
- float wipeAmount = -Mathf.Clamp(m_SplitView, -1, 1) * screen.width;
- Vector3 pos = screen.center;
- pos.x -= wipeAmount/2;
- ci.mViewport.localPosition = pos;
- ci.mViewport.localRotation = Quaternion.identity;
- ci.mViewport.localScale = Vector3.one;
- ci.mViewport.ForceUpdateRectTransforms();
- ci.mViewport.sizeDelta = new Vector2(screen.width + 1 - Mathf.Abs(wipeAmount), screen.height + 1);
- Vector2 scale = Vector2.one;
- if (m_Image != null
- && m_Image.width > 0 && m_Image.width > 0
- && screen.width > 0 && screen.height > 0)
- {
- float f = (screen.height * m_Image.width) / (screen.width * m_Image.height);
- switch (m_Aspect)
- {
- case FillStrategy.BestFit:
- if (f >= 1)
- scale.y /= f;
- else
- scale.x *= f;
- break;
- case FillStrategy.CropImageToFit:
- if (f >= 1)
- scale.x *= f;
- else
- scale.y /= f;
- break;
- case FillStrategy.StretchToFit:
- break;
- }
- }
- scale.x *= m_Scale.x;
- scale.y *= m_SyncScale ? m_Scale.x : m_Scale.y;
- ci.mRawImage.texture = m_Image;
- Color tintColor = Color.white;
- tintColor.a = m_Alpha * alpha;
- ci.mRawImage.color = tintColor;
- pos = new Vector2(screen.width * m_Center.x, screen.height * m_Center.y);
- pos.x += wipeAmount/2;
- ci.mRawImage.rectTransform.localPosition = pos;
- ci.mRawImage.rectTransform.localRotation = Quaternion.Euler(m_Rotation);
- ci.mRawImage.rectTransform.localScale = scale;
- ci.mRawImage.rectTransform.ForceUpdateRectTransforms();
- ci.mRawImage.rectTransform.sizeDelta = screen.size;
- }
- }
- static void StaticBlendingHandler(CinemachineBrain brain)
- {
- CameraState state = brain.CurrentCameraState;
- int numBlendables = state.NumCustomBlendables;
- for (int i = 0; i < numBlendables; ++i)
- {
- var b = state.GetCustomBlendable(i);
- CinemachineStoryboard src = b.m_Custom as CinemachineStoryboard;
- if (!(src == null)) // in case it was deleted
- {
- bool showIt = true;
- int layer = 1 << src.gameObject.layer;
- if (brain.OutputCamera == null || (brain.OutputCamera.cullingMask & layer) == 0)
- showIt = false;
- if (s_StoryboardGlobalMute)
- showIt = false;
- CanvasInfo ci = src.LocateMyCanvas(brain, showIt);
- if (ci != null)
- src.PlaceImage(ci, b.m_Weight);
- }
- }
- }
- #if UNITY_EDITOR
- [UnityEditor.InitializeOnLoad]
- class EditorInitialize { static EditorInitialize() { InitializeModule(); } }
- #endif
- [RuntimeInitializeOnLoadMethod]
- static void InitializeModule()
- {
- CinemachineCore.CameraUpdatedEvent.RemoveListener(StaticBlendingHandler);
- CinemachineCore.CameraUpdatedEvent.AddListener(StaticBlendingHandler);
- }
- #if UNITY_EDITOR
- // Workaround for the Unity bug where OnDestroy doesn't get called if Undo
- // bug case Case 1004117
- [UnityEditor.InitializeOnLoad]
- class CanvasesAndTheirOwners
- {
- static Dictionary<UnityEngine.Object, UnityEngine.Object> sCanvasesAndTheirOwners;
- static CanvasesAndTheirOwners()
- {
- UnityEditor.Undo.undoRedoPerformed -= OnUndoRedoPerformed;
- UnityEditor.Undo.undoRedoPerformed += OnUndoRedoPerformed;
- }
- static void OnUndoRedoPerformed()
- {
- if (sCanvasesAndTheirOwners != null)
- {
- List<UnityEngine.Object> toDestroy = null;
- foreach (var v in sCanvasesAndTheirOwners)
- {
- if (v.Value == null)
- {
- if (toDestroy == null)
- toDestroy = new List<UnityEngine.Object>();
- toDestroy.Add(v.Key);
- }
- }
- if (toDestroy != null)
- {
- foreach (var o in toDestroy)
- {
- RemoveCanvas(o);
- RuntimeUtility.DestroyObject(o);
- }
- }
- }
- }
- public static void RemoveCanvas(UnityEngine.Object canvas)
- {
- if (sCanvasesAndTheirOwners != null && sCanvasesAndTheirOwners.ContainsKey(canvas))
- sCanvasesAndTheirOwners.Remove(canvas);
- }
- public static void AddCanvas(UnityEngine.Object canvas, UnityEngine.Object owner)
- {
- if (sCanvasesAndTheirOwners == null)
- sCanvasesAndTheirOwners = new Dictionary<UnityEngine.Object, UnityEngine.Object>();
- sCanvasesAndTheirOwners.Add(canvas, owner);
- }
- }
- #endif
- }
- }
- #else
- // We need this dummy MonoBehaviour for Unity to properly recognize this script asset.
- namespace Cinemachine
- {
- [AddComponentMenu("")] // Hide in menu
- public class CinemachineStoryboard : MonoBehaviour {}
- }
- #endif
|