#if HAVE_SLATE
using System;
using System.Collections.Generic;
using UnityEngine;
using Slate;
using Framework.Cameras;
using Framework.Support;
namespace Framework.Cinematics {
/// Helper methods for dealing with cutscenes
public static class CutsceneHelper {
#region class CameraParenter
/// Temporarily changes the parent of a game object
private class CameraParenter : IDisposable {
/// Initializes a new instance that does nothing
public CameraParenter() {
this.onCutsceneStoppedDelegate = new Action(onCutsceneStopped);
}
///
/// Assigns a new parent to the camera and remembers the previous one
///
/// New parent for the camera
/// Camera that will be reparented
public CameraParenter(
GameObject newParentGameObject, GameObject cameraGameObject
) : this() {
Initialize(newParentGameObject, cameraGameObject);
}
///
/// Assigns a new parent to the camera and remembers the previous one
///
/// New parent for the camera
/// Camera that will be reparented
public void Initialize(
GameObject newParentGameObject, GameObject cameraGameObject
) {
this.cameraGameObject = cameraGameObject;
Transform cameraTransform = cameraGameObject.transform;
this.previousPosition = cameraTransform.localPosition;
this.previousOrientation = cameraTransform.localRotation;
Transform parentTransform = cameraTransform.parent;
if(parentTransform == null) {
this.previousParentGameObject = null;
} else {
this.previousParentGameObject = parentTransform.gameObject;
}
GameObjectHelper.SetAsChild(newParentGameObject, cameraGameObject);
cameraTransform.localPosition = Vector3.zero;
cameraTransform.localRotation = Quaternion.identity;
}
/// Restores the original parent of the camera
public void Dispose() {
WatchCutsceneForRecycling(null);
if(this.cameraGameObject != null) {
GameObjectHelper.SetAsChild(this.previousParentGameObject, this.cameraGameObject);
Transform cameraTransform = this.cameraGameObject.transform;
cameraTransform.localPosition = this.previousPosition;
cameraTransform.localRotation = this.previousOrientation;
this.cameraGameObject = null;
}
}
/// Sets the parenter up for recycling after a cutscene finishes
/// Cutscene that will be watched for completion
public void WatchCutsceneForRecycling(Cutscene cutscene) {
if(this.cutsceneForUnsubscribe != null) {
Cutscene.OnCutsceneStopped -= this.onCutsceneStoppedDelegate;
this.cutsceneForUnsubscribe = null;
}
if(cutscene != null) {
Cutscene.OnCutsceneStopped += this.onCutsceneStoppedDelegate;
this.cutsceneForUnsubscribe = cutscene;
}
}
/// Recycles the parenter when the cutscene stops playing
/// Cutscene that has stopped playing
private void onCutsceneStopped(Cutscene cutscene) {
if(ReferenceEquals(this.cutsceneForUnsubscribe, cutscene)) {
Dispose();
CutsceneHelper.recyclableCameraParenters.Add(this);
}
}
/// Game object that will be reparented
private GameObject cameraGameObject;
/// Game object that was the camera's previous parent
private GameObject previousParentGameObject;
/// Previous position of the camera
private Vector3 previousPosition;
/// Previous orientation of the camera
private Quaternion previousOrientation;
/// Cutscene this camera parenter is watching for completion
private Cutscene cutsceneForUnsubscribe;
/// Delegate for the onCutsceneStopped() method
private Action onCutsceneStoppedDelegate;
}
#endregion // class CameraParenter
/// Starts playing a cutscene using the specified camera
/// Cutscene that will be played
/// Camera on which the cutscene will be played
/// Callback to invoke when the cutscene ends
public static void PlayCutsceneOnCamera(
Cutscene cutscene, CameraController cameraController, System.Action callback = null
) {
// Stop the DirectorCamera from messing with our camera settings
DirectorCamera.setMainWhenActive = false;
DirectorCamera.matchMainWhenActive = false;
DirectorCamera.autoHandleActiveState = false;
DirectorCamera.dontDestroyOnLoad = false;
// Parent our render camera to the director camera root
CameraParenter parenter = recycleOrCreateCameraParenter();
parenter.Initialize(DirectorCamera.current.gameObject, cameraController.gameObject);
// Set up the parenter to recycle (and unparent) when the cutscene ends,
// then play the cutscene (in this order in case of a zero-length cutscene)
parenter.WatchCutsceneForRecycling(cutscene);
// Start the cutscene. This will disable the active camera, but we'll undo
// that little mishap right away.
cutscene.Play(0.0f, callback);
// Switch to our own render camera
CameraHelper.ActivateCamera(cameraController.Camera);
}
/// Starts playing a cutscene using the director camera
/// Cutscene that will be played
/// Camera that will be active when the cutscene starts
/// Callback to invoke when the cutscene ends
public static void PlayCutsceneOnDirectorCamera(
Cutscene cutscene, CameraController cameraController, System.Action callback = null
) {
// In this mode, we want the DirectorCamera to copy the camera's settings
DirectorCamera.setMainWhenActive = true;
DirectorCamera.matchMainWhenActive = true;
DirectorCamera.autoHandleActiveState = true;
DirectorCamera.dontDestroyOnLoad = false;
// Active the camera from which the DirectorCamera should copy its settings
CameraHelper.ActivateCamera(cameraController.Camera);
// Start the cutscene
cutscene.Play(0.0f, callback);
}
///
/// Returns a recycled instance of a camera parenter or creates a new one
///
/// An unused instance of a camera parenter
private static CameraParenter recycleOrCreateCameraParenter() {
if(recyclableCameraParenters == null) {
recyclableCameraParenters = new List();
return new CameraParenter();
}
int parenterCount = recyclableCameraParenters.Count;
if(parenterCount == 0) {
return new CameraParenter();
}
CameraParenter parenter = recyclableCameraParenters[parenterCount - 1];
recyclableCameraParenters.RemoveAt(parenterCount - 1);
return parenter;
}
/// Camera parenters that are ready for reuse
private static IList recyclableCameraParenters;
}
} // namespace Framework.Cinematics
#endif // HAVE_SLATE