#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