cz.xprees.scene-management 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/.github/workflows/deploy.yml +32 -0
  2. package/Editor/EditorSceneLoader.cs +43 -0
  3. package/Editor/EditorSceneLoader.cs.meta +3 -0
  4. package/Editor/EditorSceneTracker.cs +52 -0
  5. package/Editor/EditorSceneTracker.cs.meta +3 -0
  6. package/Editor/SceneDropdownEditor.cs +64 -0
  7. package/Editor/SceneDropdownEditor.cs.meta +3 -0
  8. package/Editor/SceneSOExtensions.cs +26 -0
  9. package/Editor/SceneSOExtensions.cs.meta +3 -0
  10. package/Editor/cz.xprees.scene-management.editor.asmdef +24 -0
  11. package/Editor/cz.xprees.scene-management.editor.asmdef.meta +3 -0
  12. package/Editor.meta +3 -0
  13. package/Runtime/Events/ScriptableObjects/SceneEventChannelSO.cs +14 -0
  14. package/Runtime/Events/ScriptableObjects/SceneEventChannelSO.cs.meta +3 -0
  15. package/Runtime/Events/ScriptableObjects.meta +3 -0
  16. package/Runtime/Events.meta +8 -0
  17. package/Runtime/Extensions/SceneInstanceExtensions.cs +16 -0
  18. package/Runtime/Extensions/SceneInstanceExtensions.cs.meta +3 -0
  19. package/Runtime/Extensions.meta +3 -0
  20. package/Runtime/Initialization/InitializationHandlers/AbstractInitializationHandlerSO.cs +44 -0
  21. package/Runtime/Initialization/InitializationHandlers/AbstractInitializationHandlerSO.cs.meta +3 -0
  22. package/Runtime/Initialization/InitializationHandlers/DesktopInitializationHandlerSO.cs +91 -0
  23. package/Runtime/Initialization/InitializationHandlers/DesktopInitializationHandlerSO.cs.meta +3 -0
  24. package/Runtime/Initialization/InitializationHandlers/IInitializationHandler.cs +27 -0
  25. package/Runtime/Initialization/InitializationHandlers/IInitializationHandler.cs.meta +3 -0
  26. package/Runtime/Initialization/InitializationHandlers.meta +3 -0
  27. package/Runtime/Initialization/InitializationLoader.cs +122 -0
  28. package/Runtime/Initialization/InitializationLoader.cs.meta +11 -0
  29. package/Runtime/Initialization.meta +3 -0
  30. package/Runtime/LoadedScenesTracker.cs +42 -0
  31. package/Runtime/LoadedScenesTracker.cs.meta +11 -0
  32. package/Runtime/SceneLoader.EventHandling.cs +171 -0
  33. package/Runtime/SceneLoader.EventHandling.cs.meta +3 -0
  34. package/Runtime/SceneLoader.InputHandling.cs +28 -0
  35. package/Runtime/SceneLoader.InputHandling.cs.meta +3 -0
  36. package/Runtime/SceneLoader.cs +151 -0
  37. package/Runtime/SceneLoader.cs.meta +11 -0
  38. package/Runtime/SceneType.cs +25 -0
  39. package/Runtime/SceneType.cs.meta +11 -0
  40. package/Runtime/ScriptableObjects/SceneSO.cs +45 -0
  41. package/Runtime/ScriptableObjects/SceneSO.cs.meta +11 -0
  42. package/Runtime/ScriptableObjects.meta +8 -0
  43. package/Runtime/Utils/OnStartScenesLoader.cs +48 -0
  44. package/Runtime/Utils/OnStartScenesLoader.cs.meta +3 -0
  45. package/Runtime/Utils.meta +3 -0
  46. package/Runtime/cz.xprees.scene-management.asmdef +24 -0
  47. package/Runtime/cz.xprees.scene-management.asmdef.meta +7 -0
  48. package/Runtime.meta +8 -0
  49. package/package.json +31 -0
  50. package/package.json.meta +7 -0
@@ -0,0 +1,32 @@
1
+ name: Publish to NPM registry
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+ - release/*
8
+
9
+ jobs:
10
+ publish-gpr:
11
+ runs-on: ubuntu-latest
12
+ permissions:
13
+ contents: read
14
+ packages: write
15
+ environment: Production
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - uses: actions/setup-node@v4
19
+ with:
20
+ node-version: 20
21
+ - name: Version Patcher
22
+ uses: justalemon/VersionPatcher@v0.8
23
+ with:
24
+ version: 1.0.${{ github.run_number }}
25
+ npm-files: 'package.json'
26
+ - uses: JS-DevTools/npm-publish@v3
27
+ with:
28
+ token: ${{ secrets.NPM_AUTH_TOKEN }}
29
+ access: public
30
+ tag: ${{ github.ref_name }}
31
+ ignore-scripts: true
32
+
@@ -0,0 +1,43 @@
1
+ using UnityEditor.SceneManagement;
2
+ using UnityEngine.SceneManagement;
3
+ using Xprees.SceneManagement.ScriptableObjects;
4
+
5
+ namespace Xprees.SceneManagement.Editor
6
+ {
7
+ public static class EditorSceneLoader
8
+ {
9
+ public static void OpenScene(SceneSO scene)
10
+ {
11
+ lock (scene) scene.IsBeingProcessed = true;
12
+
13
+ EditorSceneManager.OpenScene(scene.GetScenePath(), OpenSceneMode.Additive);
14
+
15
+ lock (scene) scene.IsBeingProcessed = false;
16
+ scene.IsLoaded = true;
17
+ }
18
+
19
+ public static void CloseScene(SceneSO scene)
20
+ {
21
+ lock (scene) scene.IsBeingProcessed = true;
22
+ var sceneByPath = SceneManager.GetSceneByPath(scene.GetScenePath());
23
+ if (sceneByPath.isDirty)
24
+ {
25
+ EditorSceneManager.SaveModifiedScenesIfUserWantsTo(new[] { sceneByPath });
26
+ }
27
+
28
+ var loadedSceneCount = SceneManager.sceneCount;
29
+ // Try to open initialization scene if no other scene is loaded
30
+ if (loadedSceneCount <= 1)
31
+ {
32
+ var initialScene = SceneManager.GetSceneByBuildIndex(0);
33
+ if (!initialScene.IsValid()) initialScene = SceneManager.GetSceneByPath("Assets/Scenes/Initialization.unity");
34
+ if (initialScene.IsValid()) EditorSceneManager.OpenScene(initialScene.path, OpenSceneMode.Single);
35
+ }
36
+
37
+ EditorSceneManager.CloseScene(sceneByPath, true);
38
+
39
+ lock (scene) scene.IsBeingProcessed = false;
40
+ scene.IsLoaded = false;
41
+ }
42
+ }
43
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: e44b9de3e7de4f2c967ff29697c70223
3
+ timeCreated: 1691927953
@@ -0,0 +1,52 @@
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using System.Linq;
4
+ using UnityEditor;
5
+ using UnityEngine.SceneManagement;
6
+ using Xprees.SceneManagement.ScriptableObjects;
7
+
8
+ namespace Xprees.SceneManagement.Editor
9
+ {
10
+ public class EditorSceneTracker
11
+ {
12
+ private string[] scenePaths;
13
+ private List<Tuple<SceneSO, bool>> scenesTracked;
14
+
15
+ public IEnumerable<SceneSO> Scenes => scenesTracked?.Select(tuple => tuple.Item1);
16
+
17
+ public EditorSceneTracker()
18
+ {
19
+ InitScenesList();
20
+ }
21
+
22
+ public bool? IsSceneOpen(SceneSO scene) => IsSceneOpen(scene.GetScenePath());
23
+
24
+ private bool IsSceneOpen(string scenePath)
25
+ {
26
+ if (scenePath == null) return false;
27
+
28
+ for (var i = 0; i < SceneManager.sceneCount; i++)
29
+ {
30
+ var currentScene = SceneManager.GetSceneAt(i);
31
+ if (currentScene.path == scenePath) return true;
32
+ }
33
+
34
+ return false;
35
+ }
36
+
37
+ private void InitScenesList()
38
+ {
39
+ scenePaths = GetAllScenePaths().ToArray();
40
+ var scenes = scenePaths.Select(AssetDatabase.LoadAssetAtPath<SceneSO>);
41
+ scenesTracked = scenes
42
+ .Select(scene => new Tuple<SceneSO, bool?>(scene, IsSceneOpen(scene)))
43
+ .Where(tuple => tuple.Item2 != null)
44
+ .Select(tuple => new Tuple<SceneSO, bool>(tuple.Item1, tuple.Item2!.Value))
45
+ .ToList();
46
+ }
47
+
48
+ private static IEnumerable<string> GetAllScenePaths() =>
49
+ AssetDatabase.FindAssets($"t:{nameof(SceneSO)}")
50
+ .Select(AssetDatabase.GUIDToAssetPath);
51
+ }
52
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 0ae03e579faa440d962d3fd43204a8a5
3
+ timeCreated: 1691927286
@@ -0,0 +1,64 @@
1
+ using System.Linq;
2
+ using UnityEditor;
3
+ using UnityEngine;
4
+ using UnityToolbarExtender;
5
+ using Xprees.SceneManagement.ScriptableObjects;
6
+
7
+ namespace Xprees.SceneManagement.Editor
8
+ {
9
+ [InitializeOnLoad]
10
+ public static class SceneDropdownEditor
11
+ {
12
+ private readonly static EditorSceneTracker sceneTracker;
13
+
14
+ static SceneDropdownEditor()
15
+ {
16
+ sceneTracker = new EditorSceneTracker();
17
+ ToolbarExtender.LeftToolbarGUI.Add(OnToolbarGUI);
18
+ }
19
+
20
+ #region GUI
21
+
22
+ private static void OnToolbarGUI()
23
+ {
24
+ GUILayout.Space(5);
25
+
26
+ // Create the dropdown button
27
+ if (GUILayout.Button(GetTitle(), EditorStyles.toolbarDropDown))
28
+ {
29
+ var menu = new GenericMenu();
30
+
31
+ foreach (var scene in sceneTracker.Scenes.OrderByDescending(scene => scene.sceneType))
32
+ {
33
+ var isSceneOpenOrPresent = sceneTracker.IsSceneOpen(scene);
34
+ if (isSceneOpenOrPresent == null) continue;
35
+
36
+ menu.AddItem(new GUIContent($"{scene.sceneName} ({scene.sceneType})"), isSceneOpenOrPresent!.Value, MenuFunction);
37
+ continue;
38
+
39
+ void MenuFunction() => ProcessScene(scene);
40
+ }
41
+
42
+ menu.ShowAsContext();
43
+ }
44
+
45
+ GUILayout.FlexibleSpace();
46
+ }
47
+
48
+ private static GUIContent GetTitle() =>
49
+ new("Scenes", EditorGUIUtility.IconContent("BuildSettings.Editor").image);
50
+
51
+ #endregion
52
+
53
+ private static void ProcessScene(SceneSO scene)
54
+ {
55
+ if (sceneTracker.IsSceneOpen(scene) ?? false)
56
+ {
57
+ EditorSceneLoader.CloseScene(scene);
58
+ return;
59
+ }
60
+
61
+ EditorSceneLoader.OpenScene(scene);
62
+ }
63
+ }
64
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 93815d5238d848d0949d91b718287ee0
3
+ timeCreated: 1691915811
@@ -0,0 +1,26 @@
1
+ using UnityEditor;
2
+ using UnityEditor.AddressableAssets;
3
+ using UnityEngine;
4
+ using Xprees.SceneManagement.ScriptableObjects;
5
+
6
+ namespace Xprees.SceneManagement.Editor
7
+ {
8
+ public static class SceneSOExtensions
9
+ {
10
+ public static string GetScenePath(this SceneSO scene)
11
+ {
12
+ var settings = AddressableAssetSettingsDefaultObject.Settings;
13
+ if (settings == null)
14
+ {
15
+ Debug.LogError("AddressableAssetSettings not found.");
16
+ return null;
17
+ }
18
+
19
+ var entry = settings.FindAssetEntry(scene.sceneReference.AssetGUID);
20
+ if (entry != null) return AssetDatabase.GUIDToAssetPath(entry.guid);
21
+
22
+ Debug.LogWarning($"Asset entry for \"{scene.name}\" not found.");
23
+ return null;
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 9dab6ca2a57f41c58fd6b1e0f0e3d3b0
3
+ timeCreated: 1692134336
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "cz.xprees.scene-management.editor",
3
+ "rootNamespace": "Xprees.SceneManagement",
4
+ "references": [
5
+ "GUID:9e24947de15b9834991c9d8411ea37cf",
6
+ "GUID:69448af7b92c7f342b298e06a37122aa",
7
+ "GUID:84651a3751eca9349aac36a66bba901b",
8
+ "GUID:0a0471484b079be408cb3a0d7fe94736",
9
+ "GUID:f88fb04354076c646a4107a491394033",
10
+ "GUID:999c2ca78ab34358a1222750376a501c",
11
+ "GUID:8abb55218ac46bd4397b3429181cd212"
12
+ ],
13
+ "includePlatforms": [
14
+ "Editor"
15
+ ],
16
+ "excludePlatforms": [],
17
+ "allowUnsafeCode": false,
18
+ "overrideReferences": false,
19
+ "precompiledReferences": [],
20
+ "autoReferenced": true,
21
+ "defineConstraints": [],
22
+ "versionDefines": [],
23
+ "noEngineReferences": false
24
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 8b99555fa7214f4fa3ce9c3de43bc9e0
3
+ timeCreated: 1691914996
package/Editor.meta ADDED
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 37744e6193814716a9af6dfbdbe049f3
3
+ timeCreated: 1691914989
@@ -0,0 +1,14 @@
1
+ using UnityEngine;
2
+ using Xprees.Events.ScriptableObjects.Base;
3
+ using Xprees.SceneManagement.ScriptableObjects;
4
+
5
+ namespace Xprees.SceneManagement.Events.ScriptableObjects
6
+ {
7
+ [CreateAssetMenu(menuName = "Events/Scene Event", fileName = "SceneEvent")]
8
+ public class SceneEventChannelSO : EventChannelBaseSO<SceneSO, bool, bool>
9
+ {
10
+ // this method is hiding the base method, but it's not overriding it -> for better readability of parameters
11
+ public new void RaiseEvent(SceneSO scene, bool showTransitionScreen, bool showLoading) =>
12
+ base.RaiseEvent(scene, showTransitionScreen, showLoading);
13
+ }
14
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: acc117d6a8d540b8904ddb61c5b342b8
3
+ timeCreated: 1679000647
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: b54aca5ce2c44a79afff9caaf4d4a1cd
3
+ timeCreated: 1679516331
@@ -0,0 +1,8 @@
1
+ fileFormatVersion: 2
2
+ guid: 0056a236c6abc6f4b89b6387539af308
3
+ folderAsset: yes
4
+ DefaultImporter:
5
+ externalObjects: {}
6
+ userData:
7
+ assetBundleName:
8
+ assetBundleVariant:
@@ -0,0 +1,16 @@
1
+ using UnityEngine.ResourceManagement.ResourceProviders;
2
+ using UnityEngine.SceneManagement;
3
+
4
+ namespace Xprees.SceneManagement.Extensions
5
+ {
6
+ public static class SceneInstanceExtensions
7
+ {
8
+ /// Sets the scene as active scene. If the scene is not valid, nothing happens.
9
+ public static void SetAsActiveScene(this SceneInstance scene)
10
+ {
11
+ if (!scene.Scene.IsValid()) return;
12
+ SceneManager.SetActiveScene(scene.Scene);
13
+ }
14
+
15
+ }
16
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: c7726e1a9dd0485abcf004d2ac44f049
3
+ timeCreated: 1705855402
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 4a72f092a99b4ad0a9d7c071a04403f4
3
+ timeCreated: 1705855393
@@ -0,0 +1,44 @@
1
+ using System.Threading;
2
+ using Cysharp.Threading.Tasks;
3
+ using UnityEngine;
4
+ using Xprees.Core;
5
+
6
+ namespace Xprees.SceneManagement.Initialization.InitializationHandlers
7
+ {
8
+ /// <warning>: All references in this script must be Addressables to avoid duplication in build,
9
+ /// therefore ScriptableObjects won't be duplicated in player build and in addressables build and shared logic will work</warning>
10
+ public abstract class AbstractInitializationHandlerSO : DescriptionBaseSO, IInitializationHandler
11
+ {
12
+ [Header("Activation")]
13
+ [Tooltip("Is this initialization handler active? If not, it will be skipped.")]
14
+ [SerializeField] private bool isActive = true;
15
+
16
+ // used to restore IsActive state in OnDisable if it was changed in OnEnable (Editor doesn't reset this value)
17
+ private bool _isActiveOnEnable;
18
+
19
+ /// Is this initialization handler active? If not, it will be skipped.
20
+ public virtual bool IsActive
21
+ {
22
+ get => isActive;
23
+ set => isActive = value;
24
+ }
25
+
26
+ /// Always call base.OnEnable() in derived classes
27
+ protected virtual void OnEnable() => _isActiveOnEnable = IsActive;
28
+
29
+ /// Always call base.OnDisable() in derived classes
30
+ protected virtual void OnDisable() => IsActive = _isActiveOnEnable;
31
+
32
+ /// This method is called by InitializationLoader before InitAsync is called for all handlers.
33
+ /// Put here all checks, etc. Use this to set IsActive to false if this handler should be skipped.
34
+ public virtual UniTask InitializeHandlerAsync(CancellationToken cancellationToken = default) => UniTask.CompletedTask;
35
+
36
+ /// This method is called by InitializationLoader only for Active handlers.
37
+ /// when it's time to initialize this handler and is this handler active.
38
+ public abstract UniTask TriggerInitializationAsync(CancellationToken cancellationToken = default);
39
+
40
+
41
+ /// This method is called by InitializationLoader on all handlers when unloading them.
42
+ public abstract UniTask UnloadHandlerAsync(CancellationToken cancellationToken = default);
43
+ }
44
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: a50545f770f4430eac108edecaf9b1bb
3
+ timeCreated: 1700046039
@@ -0,0 +1,91 @@
1
+ using System.Collections.Generic;
2
+ using System.Threading;
3
+ using Cysharp.Threading.Tasks;
4
+ using UnityEngine;
5
+ using UnityEngine.AddressableAssets;
6
+ using Xprees.SceneManagement.Events.ScriptableObjects;
7
+ using Xprees.SceneManagement.ScriptableObjects;
8
+
9
+ namespace Xprees.SceneManagement.Initialization.InitializationHandlers
10
+ {
11
+ [CreateAssetMenu(menuName = "CF/Initialization/Desktop Initializer", fileName = "DesktopInitializationHandler")]
12
+ public class DesktopInitializationHandlerSO : AbstractInitializationHandlerSO
13
+ {
14
+ [Header("Scene References")]
15
+ [Tooltip("Asset reference to the menu sceneData (SceneSO)")]
16
+ [SerializeField] private AssetReferenceT<SceneSO> menuToLoadSceneDataReference;
17
+
18
+ [Space]
19
+ [Tooltip("Asset references to the any additional scenes to load for dektop mode.")]
20
+ [SerializeField] private List<AssetReferenceT<SceneSO>> additionalScenesToLoad;
21
+
22
+ [Header("Broadcasting on")]
23
+ [Tooltip("Reference to the event raised when menu should be loaded.")]
24
+ [SerializeField] private AssetReferenceT<SceneEventChannelSO> loadMenuEventAssetReference;
25
+
26
+ [Space]
27
+ [Tooltip("Reference to the event raised when additional scenes should be loaded.")]
28
+ [SerializeField] private AssetReferenceT<SceneEventChannelSO> loadAdditionalSceneEventReference;
29
+
30
+ private SceneSO _menuToLoad;
31
+
32
+ public override async UniTask TriggerInitializationAsync(CancellationToken cancellationToken = default)
33
+ {
34
+ await LoadMenu(cancellationToken);
35
+ await WaitUntilMenuIsLoaded(cancellationToken);
36
+ await LoadAdditionalScenes(cancellationToken);
37
+ }
38
+
39
+ public override UniTask UnloadHandlerAsync(CancellationToken cancellationToken = default)
40
+ {
41
+ _menuToLoad = null;
42
+ return UniTask.CompletedTask;
43
+ }
44
+
45
+ protected override void OnDisable()
46
+ {
47
+ base.OnDisable();
48
+ _menuToLoad = null;
49
+ }
50
+
51
+ private async UniTask LoadMenu(CancellationToken cancellationToken = default)
52
+ {
53
+ var loadMenuEventChannel = await loadMenuEventAssetReference.LoadAssetAsync<SceneEventChannelSO>()
54
+ .ToUniTask(cancellationToken: cancellationToken);
55
+ _menuToLoad = await menuToLoadSceneDataReference.LoadAssetAsync<SceneSO>()
56
+ .ToUniTask(cancellationToken: cancellationToken);
57
+
58
+ RaiseLoadMenuEvent(loadMenuEventChannel, _menuToLoad);
59
+ }
60
+
61
+ private async UniTask LoadAdditionalScenes(CancellationToken cancellationToken)
62
+ {
63
+ var loadAdditionalSceneEventChannel = await loadAdditionalSceneEventReference
64
+ .LoadAssetAsync<SceneEventChannelSO>().ToUniTask(cancellationToken: cancellationToken);
65
+
66
+ foreach (var sceneRef in additionalScenesToLoad)
67
+ {
68
+ var scene = await sceneRef.LoadAssetAsync<SceneSO>().ToUniTask(cancellationToken: cancellationToken);
69
+ RaiseLoadMenuEvent(loadAdditionalSceneEventChannel, scene);
70
+ }
71
+
72
+ // We don't need to wait for additional scenes to load - Don't care
73
+ }
74
+
75
+ private void RaiseLoadMenuEvent(SceneEventChannelSO loadMenuEventChannel, SceneSO menuToLoad)
76
+ {
77
+ if (loadMenuEventChannel == null)
78
+ {
79
+ Debug.LogError("Menu load event channel is null. Cannot load menu.");
80
+ return;
81
+ }
82
+
83
+ loadMenuEventChannel.RaiseEvent(menuToLoad, true, false);
84
+ }
85
+
86
+ private UniTask WaitUntilMenuIsLoaded(CancellationToken cancellationToken = default) =>
87
+ UniTask.WaitUntil(IsMenuLoaded, cancellationToken: cancellationToken).SuppressCancellationThrow();
88
+
89
+ private bool IsMenuLoaded() => _menuToLoad != null && _menuToLoad.IsLoaded && _menuToLoad.sceneInstance.HasValue;
90
+ }
91
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 28b27e60545f4be1b4af615545fce7a9
3
+ timeCreated: 1700049572
@@ -0,0 +1,27 @@
1
+ using System.Threading;
2
+ using Cysharp.Threading.Tasks;
3
+
4
+ namespace Xprees.SceneManagement.Initialization.InitializationHandlers
5
+ {
6
+ /// This interface defines the contract for initialization handlers.
7
+ /// Their lifecycle is managed by InitializationLoader.
8
+ /// First InitializeHandlerAsync is called on all handlers.
9
+ /// Second TriggerInitializationAsync is called on all active handlers.
10
+ /// Finally on scene unload UnloadHandlerAsync is called on all handlers.
11
+ public interface IInitializationHandler
12
+ {
13
+ /// Is this initialization handler active? If not, it will be skipped.
14
+ bool IsActive { get; set; }
15
+
16
+ /// This method is called by InitializationLoader before InitAsync is called.
17
+ /// Put here all checks, etc. Use this to set IsActive to false if this handler should be skipped.
18
+ UniTask InitializeHandlerAsync(CancellationToken cancellationToken = default);
19
+
20
+ /// This method is called by InitializationLoader
21
+ /// when it's time to initialize this handler and is this handler active.
22
+ UniTask TriggerInitializationAsync(CancellationToken cancellationToken = default);
23
+
24
+ /// This method is called by InitializationLoader when unloading this handler.
25
+ UniTask UnloadHandlerAsync(CancellationToken cancellationToken = default);
26
+ }
27
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: bc8414810c1f4d88a6f0acbd44a54174
3
+ timeCreated: 1700052480
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 4fc415230998452a8fb70e96fa4d449d
3
+ timeCreated: 1700087663
@@ -0,0 +1,122 @@
1
+ using System.Collections.Generic;
2
+ using System.Linq;
3
+ using System.Threading;
4
+ using Cysharp.Threading.Tasks;
5
+ using UnityEngine;
6
+ using UnityEngine.AddressableAssets;
7
+ using UnityEngine.SceneManagement;
8
+ using Xprees.Events.ScriptableObjects.Base;
9
+ using Xprees.SceneManagement.Extensions;
10
+ using Xprees.SceneManagement.Initialization.InitializationHandlers;
11
+ using Xprees.SceneManagement.ScriptableObjects;
12
+
13
+ namespace Xprees.SceneManagement.Initialization
14
+ {
15
+ public class InitializationLoader : MonoBehaviour
16
+ {
17
+ // Warning: References must be Addressables to avoid duplication in build,
18
+ // otherwise assets copies will be included one in player build and one in addressables build
19
+ [Header("Scene References")]
20
+ [Tooltip("Asset reference to the persistent managers sceneData (SceneSO)")]
21
+ [SerializeField] private AssetReferenceT<SceneSO> managersSceneDataReference;
22
+
23
+ [Header("Listening to")]
24
+ [SerializeField] private AssetReferenceT<VoidEventChannelSO> startHandlersInitializationEventRef;
25
+
26
+ [Header("Additional Initialization Handlers")]
27
+ [SerializeField] private List<AbstractInitializationHandlerSO> initializationHandlers;
28
+
29
+ private IEnumerable<AbstractInitializationHandlerSO> ActiveHandlers => initializationHandlers.Where(handler => handler.IsActive);
30
+
31
+
32
+ private VoidEventChannelSO _startHandlersInitializationEvent;
33
+
34
+ private async void Awake()
35
+ {
36
+ var token = this.GetCancellationTokenOnDestroy();
37
+
38
+ // Managers scene must be loaded first and in Awake
39
+ // to have all PersistentManagers ready for calls from the other scripts on Start
40
+ await LoadManagersScene(token)
41
+ .SuppressCancellationThrow();
42
+ CheckActiveHandlers();
43
+ }
44
+
45
+ private void OnDisable()
46
+ {
47
+ _startHandlersInitializationEvent.onEventRaised -= StartHandlersInitialization;
48
+ }
49
+
50
+ private async void StartHandlersInitialization()
51
+ {
52
+ var token = this.GetCancellationTokenOnDestroy();
53
+
54
+ await InitializeHandlers(token);
55
+
56
+ await TriggerInitHandlers(token);
57
+
58
+ await UnloadHandlers(token);
59
+
60
+ UnloadInitializationScene().Forget(); // Will also unload this script we don't need to wait for that :-)
61
+ }
62
+
63
+ private async UniTask InitializeHandlers(CancellationToken cancellationToken)
64
+ {
65
+ var initTasks = initializationHandlers
66
+ .Select(handler => handler.InitializeHandlerAsync(cancellationToken))
67
+ .ToList();
68
+
69
+ await UniTask.WhenAll(initTasks).SuppressCancellationThrow();
70
+ }
71
+
72
+ private async UniTask TriggerInitHandlers(CancellationToken cancellationToken)
73
+ {
74
+ var triggerTasks = ActiveHandlers
75
+ .Select(handler => handler.TriggerInitializationAsync(cancellationToken))
76
+ .ToList();
77
+
78
+ await UniTask.WhenAll(triggerTasks).SuppressCancellationThrow();
79
+ }
80
+
81
+ private async UniTask UnloadHandlers(CancellationToken cancellationToken)
82
+ {
83
+ var unloadTasks = initializationHandlers
84
+ .Select(handler => handler.UnloadHandlerAsync(cancellationToken))
85
+ .ToList();
86
+
87
+ await UniTask.WhenAll(unloadTasks).SuppressCancellationThrow();
88
+ }
89
+
90
+ private async UniTask LoadManagersScene(CancellationToken cancellationToken)
91
+ {
92
+ _startHandlersInitializationEvent = await startHandlersInitializationEventRef.LoadAssetAsync<VoidEventChannelSO>()
93
+ .ToUniTask(cancellationToken: cancellationToken);
94
+
95
+ _startHandlersInitializationEvent.onEventRaised += StartHandlersInitialization;
96
+
97
+ var managersScene = await managersSceneDataReference.LoadAssetAsync<SceneSO>()
98
+ .ToUniTask(cancellationToken: cancellationToken);
99
+
100
+ var managersSceneInstance = await managersScene.sceneReference.LoadSceneAsync(LoadSceneMode.Additive, true)
101
+ .ToUniTask(cancellationToken: cancellationToken);
102
+ // Set as active to make sure if there are any created objects they are in the right scene and initialization scene
103
+ managersSceneInstance.SetAsActiveScene();
104
+
105
+ await UniTask.WaitUntil(IsManagersSceneReady, cancellationToken: cancellationToken);
106
+ return;
107
+
108
+ bool IsManagersSceneReady() => managersScene.sceneInstance.HasValue && managersScene.IsLoaded;
109
+ }
110
+
111
+ private UniTask UnloadInitializationScene() => SceneManager.UnloadSceneAsync(0).ToUniTask(); // only scene in build settings
112
+
113
+ private void CheckActiveHandlers()
114
+ {
115
+ if (!ActiveHandlers.Any())
116
+ {
117
+ Debug.LogWarning($"{nameof(InitializationLoader)} has {initializationHandlers.Count} initialization handlers. " +
118
+ $"Only {ActiveHandlers.Count()} are active.");
119
+ }
120
+ }
121
+ }
122
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: a132bc1c96429fd4aa5f0a4acdbd0e69
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant:
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 18984d09c39a45cb94d6fc92be878e6f
3
+ timeCreated: 1700045952