#if CINEMACHINE_UGUI
using UnityEngine;
using System.Collections.Generic;
using UnityEngine.Serialization;
namespace Unity.Cinemachine
{
///
/// An add-on module for CinemachineCamera that places an image in screen space
/// over the camera's output.
///
[SaveDuringPlay]
[AddComponentMenu("Cinemachine/Procedural/Extensions/Cinemachine Storyboard")]
[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")]
[FormerlySerializedAs("m_ShowImage")]
public bool ShowImage = true;
///
/// The image to display
///
[Tooltip("The image to display")]
[FormerlySerializedAs("m_Image")]
public Texture 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")]
[FormerlySerializedAs("m_Aspect")]
public FillStrategy 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")]
[FormerlySerializedAs("m_Alpha")]
[Range(0, 1)]
public float 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")]
[FormerlySerializedAs("m_Center")]
public Vector2 Center = Vector2.zero;
///
/// The screen-space rotation to apply to the image
///
[Tooltip("The screen-space rotation to apply to the image")]
[FormerlySerializedAs("m_Rotation")]
public Vector3 Rotation = Vector3.zero;
///
/// The screen-space scaling to apply to the image
///
[Tooltip("The screen-space scaling to apply to the image")]
[FormerlySerializedAs("m_Scale")]
public Vector2 Scale = Vector3.one;
///
/// If checked, X and Y scale are synchronized
///
[Tooltip("If checked, X and Y scale are synchronized")]
[FormerlySerializedAs("m_SyncScale")]
public bool 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")]
[FormerlySerializedAs("m_MuteCamera")]
public bool MuteCamera;
///
/// Wipe the image on and off horizontally
///
[Range(-1, 1)]
[Tooltip("Wipe the image on and off horizontally")]
[FormerlySerializedAs("m_SplitView")]
public float 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.")]
[FormerlySerializedAs("m_RenderMode")]
public StoryboardRenderMode 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.")]
[FormerlySerializedAs("m_SortingOrder")]
public int SortingOrder;
///
/// How far away from the camera is the storyboard's canvas generated.
///
[Tooltip("How far away from the camera is the Canvas generated.")]
[FormerlySerializedAs("m_PlaneDistance")]
public float PlaneDistance = 100;
class CanvasInfo
{
public GameObject Canvas;
public Canvas CanvasComponent;
public CinemachineBrain CanvasParent;
public RectTransform Viewport; // for mViewport clipping
public UnityEngine.UI.RawImage RawImage;
}
List m_CanvasInfo = 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 != ComponentOwner || stage != CinemachineCore.Stage.Finalize)
return;
UpdateRenderCanvas();
if (ShowImage)
state.AddCustomBlendable(new CameraState.CustomBlendableItems.Item { Custom = this, Weight = 1});
if (MuteCamera)
state.BlendHint |= CameraState.BlendHints.NoTransform | CameraState.BlendHints.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 = UnityEngine.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 = UnityEngine.RenderMode.ScreenSpaceCamera
};
void UpdateRenderCanvas()
{
for (int i = 0; i < m_CanvasInfo.Count; ++i)
{
if (m_CanvasInfo[i] == null || m_CanvasInfo[i].CanvasComponent == null)
m_CanvasInfo.RemoveAt(i--);
else
{
m_CanvasInfo[i].CanvasComponent.renderMode = (RenderMode)RenderMode;
m_CanvasInfo[i].CanvasComponent.planeDistance = PlaneDistance;
m_CanvasInfo[i].CanvasComponent.sortingOrder = 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)
{
var owner = ComponentOwner;
if (owner == null)
return;
var showIt = enabled && ShowImage && CinemachineCore.IsLive(owner);
var channel = (uint)owner.OutputChannel;
if (s_StoryboardGlobalMute || ((uint)brain.ChannelMask & channel) == 0)
showIt = false;
var ci = LocateMyCanvas(brain, showIt);
if (ci != null && ci.Canvas != null)
ci.Canvas.SetActive(showIt);
}
CanvasInfo LocateMyCanvas(CinemachineBrain parent, bool createIfNotFound)
{
CanvasInfo ci = null;
for (int i = 0; ci == null && i < m_CanvasInfo.Count; ++i)
if (m_CanvasInfo[i] != null && m_CanvasInfo[i].CanvasParent == parent)
ci = m_CanvasInfo[i];
if (createIfNotFound)
{
if (ci == null)
{
ci = new CanvasInfo() { CanvasParent = parent };
int numChildren = parent.transform.childCount;
for (int i = 0; ci.Canvas == null && i < numChildren; ++i)
{
var child = parent.transform.GetChild(i) as RectTransform;
if (child != null && child.name == CanvasName)
{
ci.Canvas = child.gameObject;
var kids = ci.Canvas.GetComponentsInChildren();
ci.Viewport = kids.Length > 1 ? kids[1] : null; // 0 is mCanvas
ci.RawImage = ci.Canvas.GetComponentInChildren();
ci.CanvasComponent = ci.Canvas.GetComponent