#if !UNITY_2019_1_OR_NEWER
#define CINEMACHINE_UGUI
#endif
using UnityEngine;
#if CINEMACHINE_UGUI
using System.Collections.Generic;
using Cinemachine.Utility;
namespace Cinemachine
{
///
/// An add-on module for Cinemachine Virtual Camera that places an image in screen space
/// over the camera's output.
///
[SaveDuringPlay]
[DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)]
[AddComponentMenu("")] // Hide in menu
[ExecuteAlways]
[DisallowMultipleComponent]
[HelpURL(Documentation.BaseURL + "manual/CinemachineStoryboard.html")]
public class CinemachineStoryboard : CinemachineExtension
{
///
/// If checked, all storyboards are globally muted
///
[Tooltip("If checked, all storyboards are globally muted")]
public static bool s_StoryboardGlobalMute;
///
/// If checked, the specified image will be displayed as an overlay over the virtual camera's output
///
[Tooltip("If checked, the specified image will be displayed as an overlay over the virtual camera's output")]
public bool m_ShowImage = true;
///
/// The image to display
///
[Tooltip("The image to display")]
public Texture m_Image;
/// How to fit the image in the frame, in the event that the aspect ratios don't match
public enum FillStrategy
{
/// Image will be as large as possible on the screen, without being cropped
BestFit,
/// Image will be cropped if necessary so that the screen is entirely filled
CropImageToFit,
/// Image will be stretched to cover any aspect mismatch with the screen
StretchToFit
};
///
/// How to handle differences between image aspect and screen aspect
///
[Tooltip("How to handle differences between image aspect and screen aspect")]
public FillStrategy m_Aspect = FillStrategy.BestFit;
///
/// The opacity of the image. 0 is transparent, 1 is opaque
///
[Tooltip("The opacity of the image. 0 is transparent, 1 is opaque")]
[Range(0, 1)]
public float m_Alpha = 1;
///
/// The screen-space position at which to display the image. Zero is center
///
[Tooltip("The screen-space position at which to display the image. Zero is center")]
public Vector2 m_Center = Vector2.zero;
///
/// The screen-space rotation to apply to the image
///
[Tooltip("The screen-space rotation to apply to the image")]
public Vector3 m_Rotation = Vector3.zero;
///
/// The screen-space scaling to apply to the image
///
[Tooltip("The screen-space scaling to apply to the image")]
public Vector2 m_Scale = Vector3.one;
///
/// If checked, X and Y scale are synchronized
///
[Tooltip("If checked, X and Y scale are synchronized")]
public bool m_SyncScale = true;
///
/// If checked, Camera transform will not be controlled by this virtual camera
///
[Tooltip("If checked, Camera transform will not be controlled by this virtual camera")]
public bool m_MuteCamera;
///
/// Wipe the image on and off horizontally
///
[Range(-1, 1)]
[Tooltip("Wipe the image on and off horizontally")]
public float m_SplitView = 0f;
///
/// The render mode of the canvas on which the storyboard is drawn.
///
[Tooltip("The render mode of the canvas on which the storyboard is drawn.")]
public StoryboardRenderMode m_RenderMode = StoryboardRenderMode.ScreenSpaceOverlay;
///
/// Allows ordering canvases to render on top or below other canvases.
///
[Tooltip("Allows ordering canvases to render on top or below other canvases.")]
public int m_SortingOrder;
///
/// How far away from the camera is the storyboard's canvas generated.
///
[Tooltip("How far away from the camera is the Canvas generated.")]
public float m_PlaneDistance = 100;
class CanvasInfo
{
public GameObject mCanvas;
public Canvas mCanvasComponent;
public CinemachineBrain mCanvasParent;
public RectTransform mViewport; // for mViewport clipping
public UnityEngine.UI.RawImage mRawImage;
}
List mCanvasInfo = new List();
/// Callback to display the image
/// The virtual camera being processed
/// The current pipeline stage
/// The current virtual camera state
/// The current applicable deltaTime
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;
UpdateRenderCanvas();
if (m_ShowImage)
state.AddCustomBlendable(new CameraState.CustomBlendable(this, 1));
if (m_MuteCamera)
state.BlendHint |= CameraState.BlendHintValue.NoTransform | CameraState.BlendHintValue.NoLens;
}
///
/// Camera render modes supported by CinemachineStoryboard.
///
public enum StoryboardRenderMode
{
///
/// Renders in camera screen space. This means, that the storyboard is going to be displayed in front of
/// any objects in the scene. Equivalent to Unity's RenderMode.ScreenSpaceOverlay.
///
ScreenSpaceOverlay = RenderMode.ScreenSpaceOverlay,
///
/// Render using the vcam on which the storyboard is on. This is useful, if you'd like to render the
/// storyboard at a specific distance from the vcam. Equivalent to Unity's RenderMode.ScreenSpaceCamera.
///
ScreenSpaceCamera = RenderMode.ScreenSpaceCamera
};
void UpdateRenderCanvas()
{
for (int i = 0; i < mCanvasInfo.Count; ++i)
{
if (mCanvasInfo[i] == null || mCanvasInfo[i].mCanvasComponent == null)
mCanvasInfo.RemoveAt(i--);
else
{
mCanvasInfo[i].mCanvasComponent.renderMode = (RenderMode) m_RenderMode;
mCanvasInfo[i].mCanvasComponent.planeDistance = m_PlaneDistance;
mCanvasInfo[i].mCanvasComponent.sortingOrder = m_SortingOrder;
}
}
}
/// Connect to virtual camera. Adds/removes listener
/// True if connecting, false if disconnecting
protected override void ConnectToVcam(bool connect)
{
base.ConnectToVcam(connect);
CinemachineCore.CameraUpdatedEvent.RemoveListener(CameraUpdatedCallback);
if (connect)
CinemachineCore.CameraUpdatedEvent.AddListener(CameraUpdatedCallback);
else
DestroyCanvas();
}
string CanvasName => "_CM_canvas" + gameObject.GetInstanceID();
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] != null && 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;
var kids = ci.mCanvas.GetComponentsInChildren();
ci.mViewport = kids.Length > 1 ? kids[1] : null; // 0 is mCanvas
ci.mRawImage = ci.mCanvas.GetComponentInChildren();
ci.mCanvasComponent = ci.mCanvas.GetComponent