// Copyright (c) 2015 - 2023 Doozy Entertainment. All Rights Reserved.
// This code can only be used under the standard Unity Asset Store End User License Agreement
// A Copy of the EULA APPENDIX 1 is available at http://unity3d.com/company/legal/as_terms
using Doozy.Runtime.Common;
using Doozy.Runtime.Common.Attributes;
using Doozy.Runtime.Common.Utils;
using Doozy.Runtime.SceneManagement.Events;
using Doozy.Runtime.SceneManagement.ScriptableObjects;
using Doozy.Runtime.Signals;
using UnityEngine;
using UnityEngine.SceneManagement;
// ReSharper disable MemberCanBePrivate.Global
namespace Doozy.Runtime.SceneManagement
{
///
/// Loads and unloads scenes and has callbacks for when the active scene changed, a scene was loaded and a scene was unloaded.
///
[AddComponentMenu("Doozy/Scene Management/Scene Director")]
public class SceneDirector : SingletonBehaviour
{
#if UNITY_EDITOR
[UnityEditor.MenuItem("GameObject/Doozy/Scene Management/Scene Director", false, 9)]
private static void CreateComponent(UnityEditor.MenuCommand menuCommand)
{
GameObjectUtils.AddToScene(true, true);
}
#endif
/// Stream category name
public const string k_StreamCategory = "SceneManagement";
public const string k_StreamName = nameof(SceneDirector);
[ClearOnReload]
private static SignalStream s_stream;
/// Signal stream for this component type
public static SignalStream stream => s_stream ??= SignalsService.GetStream(k_StreamCategory, k_StreamName);
/// Reference to the SceneManagement global settings
public static SceneManagementSettings settings => SceneManagementSettings.instance;
/// Debug flag
public bool debug => DebugMode | settings.DebugMode;
/// Enable relevant debug messages to be printed to the console
public bool DebugMode;
[SerializeField] private ActiveSceneChangedEvent OnActiveSceneChanged;
/// UnityEvent executed when the active Scene has changed
public ActiveSceneChangedEvent onActiveSceneChanged => OnActiveSceneChanged ?? (OnActiveSceneChanged = new ActiveSceneChangedEvent());
[SerializeField] private SceneLoadedEvent OnSceneLoaded;
/// UnityEvent executed when a Scene has loaded
public SceneLoadedEvent onSceneLoaded => OnSceneLoaded ?? (OnSceneLoaded = new SceneLoadedEvent());
[SerializeField] private SceneUnloadedEvent OnSceneUnloaded;
/// UnityEvent executed when a Scene has unloaded
public SceneUnloadedEvent onSceneUnloaded => OnSceneUnloaded ?? (OnSceneUnloaded = new SceneUnloadedEvent());
/// Signal receiver
private SignalReceiver receiver { get; set; }
private void ProcessSignal(Signal signal)
{
if (!signal.hasValue)
return;
if (!(signal.valueAsObject is SceneLoaderSignalData data))
return;
}
protected override void Awake()
{
base.Awake();
receiver = new SignalReceiver().SetOnSignalCallback(ProcessSignal);
stream.ConnectReceiver(receiver);
}
private void OnEnable()
{
SceneManager.activeSceneChanged += ActiveSceneChanged;
SceneManager.sceneLoaded += SceneLoaded;
SceneManager.sceneUnloaded += SceneUnloaded;
}
private void OnDisable()
{
SceneManager.activeSceneChanged -= ActiveSceneChanged;
SceneManager.sceneLoaded -= SceneLoaded;
SceneManager.sceneUnloaded -= SceneUnloaded;
}
protected override void OnDestroy()
{
base.OnDestroy();
stream.DisconnectReceiver(receiver);
}
/// Method called by the SceneManager.activeSceneChanged UnityAction
/// Replaced Scene
/// Next Scene
private void ActiveSceneChanged(Scene current, Scene next)
{
onActiveSceneChanged?.Invoke(current, next);
if (debug) Log($"{ObjectNames.NicifyVariableName(nameof(ActiveSceneChanged))} - Replaced Scene: {current.name} / Next Scene: {next.name}");
}
/// Method called by the SceneManager.sceneLoaded UnityAction
/// Loaded Scene
/// LoadSceneMode used to load the scene
private void SceneLoaded(Scene scene, LoadSceneMode mode)
{
onSceneLoaded?.Invoke(scene, mode);
if (debug) Log($"{ObjectNames.NicifyVariableName(nameof(SceneLoaded))} - Scene: {scene.name} / LoadSceneMode: {mode}");
}
/// Method called by the SceneManager.sceneUnloaded UnityAction
/// Unloaded Scene
private void SceneUnloaded(Scene unloadedScene)
{
onSceneUnloaded?.Invoke(unloadedScene);
if (debug) Log($"{ObjectNames.NicifyVariableName(nameof(SceneUnloaded))} - Scene: {unloadedScene.name}");
}
/// Create a SceneLoader that loads the Scene asynchronously in the background by its index in Build Settings, then returns a reference to the newly created SceneLoader
/// Index of the Scene in the Build Settings to load
/// If LoadSceneMode.Single then all current Scenes will be unloaded before loading
public static SceneLoader LoadSceneAsync(int sceneBuildIndex, LoadSceneMode loadSceneMode)
{
if (instance.debug) Log($"{ObjectNames.NicifyVariableName(nameof(LoadSceneAsync))} - sceneBuildIndex: {sceneBuildIndex} / loadSceneMode: {loadSceneMode}", instance);
return
SceneLoader
.GetLoader()
.SetSceneBuildIndex(sceneBuildIndex)
.SetLoadSceneBy(GetSceneBy.BuildIndex)
.SetLoadSceneMode(loadSceneMode)
.LoadSceneAsync();
}
/// Create a SceneLoader that loads the Scene asynchronously in the background by its name in Build Settings, then returns a reference to the newly created SceneLoader
/// Name or path of the Scene to load
/// If LoadSceneMode.Single then all current Scenes will be unloaded before loading
public static SceneLoader LoadSceneAsync(string sceneName, LoadSceneMode loadSceneMode)
{
if (instance.debug) Log($"{ObjectNames.NicifyVariableName(nameof(LoadSceneAsync))} - sceneName: {sceneName} / loadSceneMode: {loadSceneMode}", instance);
return
SceneLoader
.GetLoader()
.SetSceneName(sceneName)
.SetLoadSceneBy(GetSceneBy.Name)
.SetLoadSceneMode(loadSceneMode)
.LoadSceneAsync();
}
///
/// Create a SceneLoader that loads the given Scene asynchronously in the background, then returns a reference to the newly created SceneLoader
///
/// Scene to load
/// If LoadSceneMode.Single then all current Scenes will be unloaded before loading
///
public static SceneLoader LoadSceneAsync(Scene scene, LoadSceneMode loadSceneMode)
{
if (instance.debug) Log($"{ObjectNames.NicifyVariableName(nameof(LoadSceneAsync))} - scene: {scene.name} / loadSceneMode: {loadSceneMode}", instance);
return
SceneLoader
.GetLoader()
.LoadSceneAsync(scene, loadSceneMode);
}
/// Destroys all GameObjects associated with the given Scene and removes the Scene from the SceneManager
/// Scene to unload.
public static AsyncOperation UnloadSceneAsync(Scene scene)
{
if (instance.debug) Log($"{ObjectNames.NicifyVariableName(nameof(UnloadSceneAsync))} - scene: {scene.name}", instance);
return scene.IsValid() ? SceneManager.UnloadSceneAsync(scene) : null;
}
/// Destroys all GameObjects associated with the given Scene and removes the Scene from the SceneManager
/// Index of the Scene in BuildSettings
public static AsyncOperation UnloadSceneAsync(int sceneBuildIndex)
{
if (instance.debug) Log($"{ObjectNames.NicifyVariableName(nameof(UnloadSceneAsync))} - sceneBuildIndex: {sceneBuildIndex}", instance);
return
SceneManager
.GetSceneByBuildIndex(sceneBuildIndex).IsValid()
? SceneManager.UnloadSceneAsync(sceneBuildIndex)
: null;
}
/// Destroys all GameObjects associated with the given Scene and removes the Scene from the SceneManager
/// Name or path of the Scene to unload.
public static AsyncOperation UnloadSceneAsync(string sceneName)
{
if (instance.debug) Log($"{ObjectNames.NicifyVariableName(nameof(UnloadSceneAsync))} - sceneName: {sceneName}", instance);
return
SceneManager
.GetSceneByName(sceneName).IsValid()
? SceneManager.UnloadSceneAsync(sceneName)
: null;
}
// ReSharper disable once MemberCanBePrivate.Global
/// Adds SceneDirector to scene and returns a reference to it
public static SceneDirector AddToScene(bool selectGameObjectAfterCreation = false) =>
GameObjectUtils.AddToScene(true, selectGameObjectAfterCreation);
private void Log(string message) =>
Log($"[{name}] {message}", this);
private static void Log(string message, Object context) =>
Debugger.Log($"({nameof(SceneDirector)}) {message}", context);
}
}