using Bootstrap; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.SceneManagement; namespace Editor.Bootstrap { /// /// Edit-mode utilities. /// /// This class provides a handy mechanism to enable editor bootstrapping. /// When enabled, the bootstrap system will initialise during edit-mode, allowing /// developers to preview the effects of the bootstrap system. /// /// There are several caveats here: /// 1. DontDestroyOnLoad doesn't work in edit-mode. Any objects loaded by the bootstrap system /// will be added to the current scene. /// 2. Entering playmode will cause this script to de-initialise the bootstrapper, if initialised. /// This means that playmode priority is always given to the boostrapper. /// 3. If editor bootstrapping is enabled, then the bootstrapper will de-init and re-init /// whenever the current scene changes. This may not be appropriate for all workflows! /// 4. If bootstrapping is enabled, and the current scene is saved, then the bootstrapper will be /// de-initialised prior to the scene being saved to disk, and then re-initialised, thereby /// avoiding scene pollution. /// [InitializeOnLoad] public static class CustomBootEditorUtils { /// /// Editor prefs key for the edit-mode bootstrapper. /// private const string INITIALISE_IN_EDITOR = "bootstrap.editor_init_enabled"; /// /// Menu path for the edit-mode bootstrapper /// private const string EDITOR_INIT_MENU = "Bootstrap/Editor Initialise"; static CustomBootEditorUtils() { InitPlayModeListener(); if (Application.isPlaying) return; //Don't initialise if we're about to change playmode! if (EditorInitialisationEnabled && !EditorApplication.isPlayingOrWillChangePlaymode) { DoInit(); } } /// /// Initialise the playmode change listener. /// This also sets up a listener for scene saving, so that we can /// de-init and reinit the bootstrapper when the user saves changes, thereby /// avoiding bootstrapped objects polluting the saved scene. /// private static void InitPlayModeListener() { EditorApplication.playModeStateChanged += EditorApplicationOnplayModeStateChanged; EditorSceneManager.sceneSaving += OnSceneSaving; } /// /// Handle the event. /// This de-initialises the bootstrapper, removing objects from the scene, /// and then listens for the event /// in order to re-initialise the bootstrapper. /// /// /// private static void OnSceneSaving(Scene scene, string path) { if (CustomBoot.Initialised) { DoDeInit(); EditorSceneManager.sceneSaved += EditorSceneManagerOnsceneSaved; void EditorSceneManagerOnsceneSaved(Scene scene1) { EditorSceneManager.sceneSaved -= EditorSceneManagerOnsceneSaved; CheckInit(); } } } /// /// Deinitialise the playmode change listener /// private static void DeInitPlayModeListener() { EditorApplication.playModeStateChanged -= EditorApplicationOnplayModeStateChanged; } /// /// Is play-mode bootstrapping enabled? /// private static bool EditorInitialisationEnabled { get => EditorPrefs.GetBool(INITIALISE_IN_EDITOR, false); set => EditorPrefs.SetBool(INITIALISE_IN_EDITOR, value); } /// /// Menu handler for the edit-mode bootstrapper /// [MenuItem(EDITOR_INIT_MENU)] private static void EditorInitialise() { EditorInitialisationEnabled = !EditorInitialisationEnabled; CheckInit(); } /// /// Menu validator for the edit-mode bootstrapper /// /// [MenuItem(EDITOR_INIT_MENU, true)] private static bool EditorInitialiseValidate() { Menu.SetChecked(EDITOR_INIT_MENU, EditorPrefs.GetBool(INITIALISE_IN_EDITOR, false)); return true; } /// /// Perform initialisation or de-initialisation depending on the current context /// private static void CheckInit() { if (EditorInitialisationEnabled && !CustomBoot.Initialised && !Application.isPlaying) { DoInit(); } else if (CustomBoot.Initialised) { DoDeInit(); } } /// /// Perform the initialisation process. /// This adds a listener for the event so that we /// can handle scene changes once we're initialised /// private static void DoInit() { EditorSceneManager.sceneClosing += OnSceneClosing; CustomBoot.PerformInitialisation(); } /// /// Handle the event /// This de-initialises the bootstrapper and then sets up a listener for the /// event so that /// we can safely re-initialise the bootstrapper /// /// /// private static void OnSceneClosing(Scene scene, bool removingscene) { if (!Application.isPlaying && CustomBoot.Initialised) { DoDeInit(); EditorSceneManager.activeSceneChangedInEditMode += OnSceneLoaded; } } /// /// Handles the event, /// allowing the boostrapper to reinitialise if required /// /// /// private static void OnSceneLoaded(Scene arg0, Scene scene) { if (!Application.isPlaying) { EditorSceneManager.activeSceneChangedInEditMode -= OnSceneLoaded; CheckInit(); } } /// /// De-Initialises the bootstrapper /// private static void DoDeInit() { CustomBoot.PerformDeInitialisation(); EditorSceneManager.sceneClosing -= OnSceneClosing; } /// /// Handles playmode change events. This will de-initialise the bootstrapper /// when exiting edit-mode, and call when entering edit-mode /// /// private static void EditorApplicationOnplayModeStateChanged(PlayModeStateChange obj) { switch (obj) { case PlayModeStateChange.ExitingEditMode: if (CustomBoot.Initialised) { DoDeInit(); } break; case PlayModeStateChange.EnteredEditMode: CheckInit(); break; } } } }