using System; using System.Collections.Generic; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.Playables; using UnityEngine.SceneManagement; using UnityEngine.Sequences; using UObject = UnityEngine.Object; using PrefabStageUtility = UnityEditor.SceneManagement.PrefabStageUtility; using PrefabStage = UnityEditor.SceneManagement.PrefabStage; namespace UnityEditor.Sequences { /// /// Interface to help retrieve cached objects. /// Caches are invalidated at every Editor tick. /// static class ObjectsCache { /// /// Interface to implement for any caches that would be compatible with /// interface ICache { IReadOnlyCollection references { get; } bool shouldRebuild { get; } void InvalidateData(); void Build(); void RemoveInvalidReferences(); } abstract class StageObjectsCache : ICache where T : UObject { static List s_RootGameObjectsBuffer = new List(); static List s_ObjectBuffer = new List(); protected List m_References = new List(); bool ICache.shouldRebuild => m_References.Count == 0; IReadOnlyCollection ICache.references => m_References; public abstract IReadOnlyList GetScenes(); public void Build() { var scenes = GetScenes(); for (int i = 0; i < scenes.Count; ++i) { // Skip scenes that are visible but unloaded in the Hierarchy. if (!scenes[i].isLoaded) continue; scenes[i].GetRootGameObjects(s_RootGameObjectsBuffer); foreach (GameObject root in s_RootGameObjectsBuffer) { root.GetComponentsInChildren(true, s_ObjectBuffer); m_References.AddRange(s_ObjectBuffer); s_ObjectBuffer.Clear(); } s_RootGameObjectsBuffer.Clear(); } } public virtual void InvalidateData() { m_References.Clear(); } public virtual void RemoveInvalidReferences() { m_References.RemoveAll(obj => obj == null); } } /// /// Implementation of for the Editor Main Stage. /// It holds references living in the Main Stage loaded scenes. /// /// Type derived from . class MainStageObjectsCache : StageObjectsCache where T : UObject { List m_SceneCache = new List(); public override IReadOnlyList GetScenes() { m_SceneCache.Clear(); for (int i = 0; i < EditorSceneManager.sceneCount; ++i) { var scene = EditorSceneManager.GetSceneAt(i); m_SceneCache.Add(scene); } return m_SceneCache; } } /// /// Implementation of for the Editor Prefab Stage. /// It holds references living in the Prefab Stage loaded scenes. /// /// Type derived from . class PrefabStageObjectsCache : StageObjectsCache where T : UObject { Scene[] m_SceneCache = new Scene[1]; public override IReadOnlyList GetScenes() { var stage = PrefabStageUtility.GetCurrentPrefabStage(); m_SceneCache[0] = stage.scene; return m_SceneCache; } } /// /// System holding the various caches. /// class ObjectsCacheSystem { enum CacheType { MainStage, PrefabStage } CacheType cacheType = CacheType.MainStage; Dictionary m_Caches = new Dictionary(); internal IReadOnlyCollection GetObjects() where T : UObject { Type type = typeof(T); // Create cache for the given type if it does not exist. if (!m_Caches.ContainsKey(type)) { if (cacheType == CacheType.PrefabStage) m_Caches.Add(type, new PrefabStageObjectsCache()); else m_Caches.Add(type, new MainStageObjectsCache()); } ICache cache = m_Caches[type]; // Rebuild cache if it has been invalidated or is empty. if (cache.shouldRebuild) cache.Build(); else cache.RemoveInvalidReferences(); return cache.references as IReadOnlyCollection; } internal void InvalidateCaches() { foreach (KeyValuePair cache in m_Caches) cache.Value.InvalidateData(); } internal void SwapToMainStageCache() { m_Caches.Clear(); cacheType = CacheType.MainStage; } internal void SwapToPrefabStageCache() { m_Caches.Clear(); cacheType = CacheType.PrefabStage; } } static ObjectsCacheSystem m_Internal; /// /// Initialize the internal cache system when the Editor loads. /// [InitializeOnLoadMethod] static void InitializeReferencesCache() { m_Internal = new ObjectsCacheSystem(); EditorApplication.update += m_Internal.InvalidateCaches; // Listen to the following events in case data manipulation is happening from script in the same editor tick. SequenceUtility.sequenceCreated += OnSequenceCreated; SequenceUtility.sequenceDeleted += OnSequenceDeleted; SequenceAssetUtility.sequenceAssetAssignedTo += SequenceAssetUtility_sequenceAssetAssignedTo; SequenceAssetUtility.sequenceAssetRemovedFrom += SequenceAssetUtility_sequenceAssetRemovedFrom; EditorSceneManager.sceneLoaded += EditorSceneManager_sceneLoaded; EditorSceneManager.sceneUnloaded += EditorSceneManager_sceneUnloaded; EditorSceneManager.sceneOpened += EditorSceneManager_sceneOpened; PrefabStage.prefabStageOpened += PrefabStage_prefabStageOpened; PrefabStage.prefabStageClosing += PrefabStage_prefabStageClosing; // Domain reload can happen while being in Prefab Stage. // This ensures we set the proper cache. if (PrefabStageUtility.GetCurrentPrefabStage() != null) m_Internal.SwapToPrefabStageCache(); } static void PrefabStage_prefabStageClosing(PrefabStage stage) { m_Internal.SwapToMainStageCache(); } static void PrefabStage_prefabStageOpened(PrefabStage stage) { m_Internal.SwapToPrefabStageCache(); } /// /// Returns a collection of references from matching Objects from loaded scenes. /// /// Type of the requested objects. Must derived from /// A read only collection of objects found. internal static IReadOnlyCollection FindObjectsFromScenes() where T : UObject { return m_Internal.GetObjects(); } static void OnSequenceCreated(TimelineSequence sequence, MasterSequence masterSequence) { m_Internal.InvalidateCaches(); } static void OnSequenceDeleted() { m_Internal.InvalidateCaches(); } static void SequenceAssetUtility_sequenceAssetRemovedFrom(PlayableDirector obj) { m_Internal.InvalidateCaches(); } static void SequenceAssetUtility_sequenceAssetAssignedTo( GameObject prefab, GameObject instance, PlayableDirector sequenceDirector) { m_Internal.InvalidateCaches(); } static void EditorSceneManager_sceneLoaded(Scene scene, LoadSceneMode arg1) { m_Internal.InvalidateCaches(); } static void EditorSceneManager_sceneUnloaded(Scene scene) { m_Internal.InvalidateCaches(); } static void EditorSceneManager_sceneOpened(Scene scene, OpenSceneMode mode) { m_Internal.InvalidateCaches(); } } }