using System.Collections.Generic; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; using UnityEngine.Playables; using UnityEngine.SceneManagement; using UnityEngine.UI; namespace Cinematics { /// /// Handles loading, playing and unloading cinematics /// public class CinematicsManager : MonoBehaviour { private static CinematicsManager _instance; private static bool _isQuitting; private Image cinematicSprites; public PlayableAsset cinematicToPlay; // Dictionary to track addressable handles by PlayableDirector private Dictionary> _addressableHandles = new Dictionary>(); public static CinematicsManager Instance { get { if (_instance == null && Application.isPlaying && !_isQuitting) { _instance = FindAnyObjectByType(); if (_instance == null) { var go = new GameObject("CinematicsManager"); _instance = go.AddComponent(); // DontDestroyOnLoad(go); } } return _instance; } } public PlayableDirector playableDirector; private void OnEnable() { // Subscribe to application quit event to ensure cleanup Application.quitting += OnApplicationQuit; } private void OnDisable() { // Unsubscribe from application quit event Application.quitting -= OnApplicationQuit; // Clean up any remaining addressable handles when disabled ReleaseAllHandles(); } private void OnApplicationQuit() { ReleaseAllHandles(); } /// /// Plays a cinematic from an object reference and returns the PlayableDirector playing the timeline /// public PlayableDirector PlayCinematic(PlayableAsset assetToPlay) { cinematicSprites.enabled = true; playableDirector.stopped += OnPlayableDirectorStopped; playableDirector.Play(assetToPlay); Debug.Log("Playing cinematic " + assetToPlay.name); return playableDirector; } void OnPlayableDirectorStopped(PlayableDirector director) { cinematicSprites.enabled = false; Debug.Log("Cinematic stopped!"); // Release the addressable handle associated with this director ReleaseAddressableHandle(director); } /// /// Loads a playable from an asset path and plays it as a cinematic /// public PlayableDirector LoadAndPlayCinematic(string key) { // Load the asset via addressables var handle = Addressables.LoadAssetAsync(key); var result = handle.WaitForCompletion(); // Store the handle for later release _addressableHandles[playableDirector] = handle; Debug.Log($"[CinematicsManager] Loaded addressable cinematic: {key}"); return PlayCinematic(result); } /// /// Skips the currently playing cinematic if one is active /// public void SkipCurrentCinematic() { if (playableDirector != null && playableDirector.state == PlayState.Playing) { Debug.Log("Skipping current cinematic"); playableDirector.Stop(); } } /// /// Checks if a cinematic is currently playing /// public bool IsCinematicPlaying() { return playableDirector != null && playableDirector.state == PlayState.Playing; } /// /// Releases the addressable handle associated with a specific PlayableDirector /// private void ReleaseAddressableHandle(PlayableDirector director) { if (_addressableHandles.TryGetValue(director, out var handle)) { Debug.Log($"[CinematicsManager] Releasing addressable handle for cinematic"); Addressables.Release(handle); _addressableHandles.Remove(director); } } /// /// Releases all active addressable handles /// private void ReleaseAllHandles() { foreach (var handle in _addressableHandles.Values) { if (handle.IsValid()) { Addressables.Release(handle); } } _addressableHandles.Clear(); } private void Start() { if (!SceneManager.GetActiveScene().name.ToLower().Contains("mainmenu")) { return; } _instance = this; if (!SceneManager.GetActiveScene().name.ToLower().Contains("mainmenu")) { return; } playableDirector = GetComponent(); cinematicSprites = GetComponentInChildren(true); LoadAndPlayCinematic("IntroSequence"); } } }