fr.jeanf.scenemanagement 0.2.0 → 0.3.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 (52) hide show
  1. package/Runtime/AdditiveLoading/Editor/WorldManagerEditor.cs +83 -0
  2. package/Runtime/AdditiveLoading/Editor/WorldManagerEditor.cs.meta +2 -0
  3. package/Runtime/AdditiveLoading/SceneLoader.cs +89 -15
  4. package/Runtime/AdditiveLoading/WorldManager.cs +217 -75
  5. package/Runtime/DynamicLoading/FollowSystem.cs +24 -13
  6. package/Runtime/DynamicLoading/RegionConnectivity.cs +36 -0
  7. package/Runtime/DynamicLoading/VolumeSystem.cs +156 -63
  8. package/Samples/Example/RegionData/Region_00/Region_00.asset +1 -1
  9. package/Samples/Example/RegionData/Region_01/Region_01.asset +3 -3
  10. package/Samples/Example/RegionData/Region_02/Region_02.asset +3 -3
  11. package/Samples/Example/RegionData/Region_02/Zones/Zone_22.asset +1 -1
  12. package/Samples/Example/RegionData/Region_02/Zones/Zone_23.asset +1 -1
  13. package/Samples/Example/RegionData/Region_02/Zones/Zone_24.asset +1 -1
  14. package/Samples/Example/RegionData/Region_02/Zones/Zone_25.asset +1 -1
  15. package/Samples/Example/RegionData/Region_02/Zones/Zone_26.asset +1 -1
  16. package/Samples/Example/RegionData/Region_02/Zones/Zone_27.asset +1 -1
  17. package/Samples/Example/RegionData/Region_02/Zones/Zone_28.asset +1 -1
  18. package/Samples/Example/Scenes/Main.unity +5 -5
  19. package/Samples/Setup/AdditiveLoading.prefab +7 -1
  20. package/package.json +1 -1
  21. /package/Samples/Example/Scenes/01/{1.unity → 11.unity} +0 -0
  22. /package/Samples/Example/Scenes/01/{1.unity.meta → 11.unity.meta} +0 -0
  23. /package/Samples/Example/Scenes/01/{2.unity → 12.unity} +0 -0
  24. /package/Samples/Example/Scenes/01/{2.unity.meta → 12.unity.meta} +0 -0
  25. /package/Samples/Example/Scenes/01/{3.unity → 13.unity} +0 -0
  26. /package/Samples/Example/Scenes/01/{3.unity.meta → 13.unity.meta} +0 -0
  27. /package/Samples/Example/Scenes/01/{4.unity → 14.unity} +0 -0
  28. /package/Samples/Example/Scenes/01/{4.unity.meta → 14.unity.meta} +0 -0
  29. /package/Samples/Example/Scenes/01/{5.unity → 15.unity} +0 -0
  30. /package/Samples/Example/Scenes/01/{5.unity.meta → 15.unity.meta} +0 -0
  31. /package/Samples/Example/Scenes/01/{6.unity → 16.unity} +0 -0
  32. /package/Samples/Example/Scenes/01/{6.unity.meta → 16.unity.meta} +0 -0
  33. /package/Samples/Example/Scenes/01/{7.unity → 17.unity} +0 -0
  34. /package/Samples/Example/Scenes/01/{7.unity.meta → 17.unity.meta} +0 -0
  35. /package/Samples/Example/Scenes/01/{8.unity → 18.unity} +0 -0
  36. /package/Samples/Example/Scenes/01/{8.unity.meta → 18.unity.meta} +0 -0
  37. /package/Samples/Example/Scenes/02/{1.unity → 21.unity} +0 -0
  38. /package/Samples/Example/Scenes/02/{1.unity.meta → 21.unity.meta} +0 -0
  39. /package/Samples/Example/Scenes/02/{2.unity → 22.unity} +0 -0
  40. /package/Samples/Example/Scenes/02/{2.unity.meta → 22.unity.meta} +0 -0
  41. /package/Samples/Example/Scenes/02/{3.unity → 23.unity} +0 -0
  42. /package/Samples/Example/Scenes/02/{3.unity.meta → 23.unity.meta} +0 -0
  43. /package/Samples/Example/Scenes/02/{4.unity → 24.unity} +0 -0
  44. /package/Samples/Example/Scenes/02/{4.unity.meta → 24.unity.meta} +0 -0
  45. /package/Samples/Example/Scenes/02/{5.unity → 25.unity} +0 -0
  46. /package/Samples/Example/Scenes/02/{5.unity.meta → 25.unity.meta} +0 -0
  47. /package/Samples/Example/Scenes/02/{6.unity → 26.unity} +0 -0
  48. /package/Samples/Example/Scenes/02/{6.unity.meta → 26.unity.meta} +0 -0
  49. /package/Samples/Example/Scenes/02/{7.unity → 27.unity} +0 -0
  50. /package/Samples/Example/Scenes/02/{7.unity.meta → 27.unity.meta} +0 -0
  51. /package/Samples/Example/Scenes/02/{8.unity → 28.unity} +0 -0
  52. /package/Samples/Example/Scenes/02/{8.unity.meta → 28.unity.meta} +0 -0
@@ -0,0 +1,83 @@
1
+ #if UNITY_EDITOR
2
+ using UnityEngine;
3
+ using UnityEditor;
4
+
5
+ namespace jeanf.scenemanagement
6
+ {
7
+ [CustomEditor(typeof(WorldManager))]
8
+ public class WorldManagerEditor : Editor
9
+ {
10
+ public override void OnInspectorGUI()
11
+ {
12
+ DrawDefaultInspector();
13
+
14
+ var worldManager = (WorldManager)target;
15
+
16
+ EditorGUILayout.Space();
17
+ EditorGUILayout.LabelField("Current Player State", EditorStyles.boldLabel);
18
+
19
+ var currentZone = WorldManager.CurrentPlayerZone;
20
+ var currentRegion = WorldManager.CurrentPlayerRegion;
21
+
22
+ EditorGUI.BeginDisabledGroup(true);
23
+
24
+ if (currentZone != null)
25
+ {
26
+ EditorGUILayout.TextField("Current Zone", $"{currentZone.zoneName}");
27
+ }
28
+ else
29
+ {
30
+ EditorGUILayout.TextField("Current Zone", "None");
31
+ }
32
+
33
+ if (currentRegion != null)
34
+ {
35
+ EditorGUILayout.TextField("Current Region", $"{currentRegion.levelName}");
36
+ }
37
+ else
38
+ {
39
+ EditorGUILayout.TextField("Current Region", "None");
40
+ }
41
+
42
+ EditorGUI.EndDisabledGroup();
43
+
44
+ if (Application.isPlaying)
45
+ {
46
+ EditorGUILayout.Space();
47
+ if (GUILayout.Button("Debug Zone/Region Info"))
48
+ {
49
+ Debug.Log($"=== WorldManager Debug Info ===");
50
+ Debug.Log($"Current Zone: {(currentZone != null ? $"'{currentZone.zoneName}' (ID: {currentZone.id})" : "None")}");
51
+ Debug.Log($"Current Region: {(currentRegion != null ? $"'{currentRegion.levelName}' (ID: {currentRegion.id})" : "None")}");
52
+
53
+ var zoneCount = worldManager.GetType().GetField("_zoneDictionary",
54
+ System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
55
+ ?.GetValue(worldManager) as System.Collections.Generic.Dictionary<string, Zone>;
56
+
57
+ var regionCount = worldManager.GetType().GetField("_regionDictionary",
58
+ System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
59
+ ?.GetValue(worldManager) as System.Collections.Generic.Dictionary<string, Region>;
60
+
61
+ if (zoneCount != null && regionCount != null)
62
+ {
63
+ Debug.Log($"Total zones in dictionary: {zoneCount.Count}");
64
+ Debug.Log($"Total regions in dictionary: {regionCount.Count}");
65
+
66
+ Debug.Log("Available zones:");
67
+ foreach (var zone in zoneCount.Values)
68
+ {
69
+ Debug.Log($" - '{zone.zoneName}' (ID: {zone.id})");
70
+ }
71
+
72
+ Debug.Log("Available regions:");
73
+ foreach (var region in regionCount.Values)
74
+ {
75
+ Debug.Log($" - '{region.levelName}' (ID: {region.id})");
76
+ }
77
+ }
78
+ }
79
+ }
80
+ }
81
+ }
82
+ }
83
+ #endif
@@ -0,0 +1,2 @@
1
+ fileFormatVersion: 2
2
+ guid: 146fcca9a633f6a45bffd11a94703fad
@@ -12,7 +12,10 @@ namespace jeanf.scenemanagement
12
12
  {
13
13
  public bool isDebug = false;
14
14
  private CancellationTokenSource _queueCts;
15
- [SerializeField] private int maxConcurrentLoads = 2;
15
+ [SerializeField] private int maxConcurrentLoads = 2;
16
+ [SerializeField] private int gcFrameSpread = 5;
17
+ [SerializeField] private bool enableIncrementalGC = true;
18
+ [SerializeField] private float memoryFlushDelay = 0.1f;
16
19
 
17
20
  public delegate void IsLoadingDelegate(bool loadingState);
18
21
  public static IsLoadingDelegate IsLoading;
@@ -49,6 +52,7 @@ namespace jeanf.scenemanagement
49
52
 
50
53
  private bool _isProcessingLoadQueue = false;
51
54
  private bool _isProcessingUnloadQueue = false;
55
+ private bool _isFlushingMemory = false;
52
56
 
53
57
  private void OnEnable() => Subscribe();
54
58
  private void OnDisable() => Unsubscribe();
@@ -59,6 +63,7 @@ namespace jeanf.scenemanagement
59
63
  LoadSceneRequest += QueueLoadScene;
60
64
  UnLoadSceneRequest += QueueUnloadScene;
61
65
  UnloadAllScenesRequest += QueueUnloadAllScenes;
66
+ FlushScenesRequest += () => IncrementalMemoryFlush().Forget();
62
67
  }
63
68
 
64
69
  private void Unsubscribe()
@@ -66,6 +71,7 @@ namespace jeanf.scenemanagement
66
71
  LoadSceneRequest -= QueueLoadScene;
67
72
  UnLoadSceneRequest -= QueueUnloadScene;
68
73
  UnloadAllScenesRequest -= QueueUnloadAllScenes;
74
+ FlushScenesRequest = null;
69
75
  }
70
76
 
71
77
  private enum SceneOperationType { Load, Unload }
@@ -84,8 +90,6 @@ namespace jeanf.scenemanagement
84
90
 
85
91
  private void QueueUnloadAllScenes()
86
92
  {
87
- if (isDebug) Debug.Log("Unloading all scenes");
88
-
89
93
  while (_loadQueue.TryDequeue(out _)) { }
90
94
 
91
95
  _scenesToUnload.Clear();
@@ -102,14 +106,48 @@ namespace jeanf.scenemanagement
102
106
  ProcessUnloadQueue().Forget();
103
107
  }
104
108
 
105
- private async UniTask FlushMemoryAsync(CancellationToken cancellationToken)
109
+ private async UniTaskVoid IncrementalMemoryFlush()
106
110
  {
107
- if (isDebug) Debug.Log("Flushing memory");
108
-
109
- await Resources.UnloadUnusedAssets().ToUniTask(cancellationToken: cancellationToken);
110
- GC.Collect();
111
-
112
- if (isDebug) Debug.Log("Memory flush complete");
111
+ if (_isFlushingMemory) return;
112
+ _isFlushingMemory = true;
113
+
114
+ try
115
+ {
116
+ await UniTask.Delay(TimeSpan.FromSeconds(memoryFlushDelay), DelayType.Realtime);
117
+
118
+ var unloadOperation = Resources.UnloadUnusedAssets();
119
+ await unloadOperation.ToUniTask();
120
+
121
+ if (enableIncrementalGC)
122
+ {
123
+ await IncrementalGarbageCollection();
124
+ }
125
+ }
126
+ finally
127
+ {
128
+ _isFlushingMemory = false;
129
+ }
130
+ }
131
+
132
+ private async UniTask IncrementalGarbageCollection()
133
+ {
134
+ if (GC.MaxGeneration >= 2)
135
+ {
136
+ for (int generation = 0; generation <= GC.MaxGeneration; generation++)
137
+ {
138
+ GC.Collect(generation, GCCollectionMode.Optimized, false);
139
+
140
+ for (int frame = 0; frame < gcFrameSpread; frame++)
141
+ {
142
+ await UniTask.Yield();
143
+ }
144
+ }
145
+ }
146
+ else
147
+ {
148
+ GC.Collect(0, GCCollectionMode.Optimized, false);
149
+ await UniTask.Yield();
150
+ }
113
151
  }
114
152
 
115
153
  private async UniTaskVoid ProcessLoadQueue()
@@ -191,7 +229,6 @@ namespace jeanf.scenemanagement
191
229
  await UniTask.WhenAll(_operationBuffer);
192
230
  }
193
231
 
194
- // Small delay to prevent overwhelming the system
195
232
  await UniTask.Yield();
196
233
  }
197
234
  }
@@ -199,7 +236,7 @@ namespace jeanf.scenemanagement
199
236
  {
200
237
  if (_loadedScenes.Count == 0)
201
238
  {
202
- await FlushMemoryAsync(token);
239
+ IncrementalMemoryFlush().Forget();
203
240
  }
204
241
 
205
242
  _isProcessingUnloadQueue = false;
@@ -212,11 +249,9 @@ namespace jeanf.scenemanagement
212
249
 
213
250
  private async UniTask LoadSceneAsync(string sceneName, CancellationToken cancellationToken)
214
251
  {
215
- AsyncOperation loadOperation = null;
216
-
217
252
  try
218
253
  {
219
- loadOperation = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
254
+ var loadOperation = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
220
255
  loadOperation.allowSceneActivation = true;
221
256
  await loadOperation.ToUniTask(cancellationToken: cancellationToken);
222
257
  _loadedScenes.Add(sceneName);
@@ -240,5 +275,44 @@ namespace jeanf.scenemanagement
240
275
  _processingScenes.Remove(sceneName);
241
276
  }
242
277
  }
278
+
279
+ private void OnApplicationFocus(bool hasFocus)
280
+ {
281
+ if (!hasFocus && enableIncrementalGC)
282
+ {
283
+ IncrementalMemoryFlush().Forget();
284
+ }
285
+ }
286
+
287
+ private void OnApplicationPause(bool pauseStatus)
288
+ {
289
+ if (pauseStatus && enableIncrementalGC)
290
+ {
291
+ IncrementalMemoryFlush().Forget();
292
+ }
293
+ }
294
+
295
+ public void ForceMemoryFlush()
296
+ {
297
+ if (!_isFlushingMemory)
298
+ {
299
+ IncrementalMemoryFlush().Forget();
300
+ }
301
+ }
302
+
303
+ public bool IsCurrentlyLoading()
304
+ {
305
+ return _isProcessingLoadQueue || _isProcessingUnloadQueue || _isFlushingMemory;
306
+ }
307
+
308
+ public int GetLoadedSceneCount()
309
+ {
310
+ return _loadedScenes.Count;
311
+ }
312
+
313
+ public int GetPendingOperationCount()
314
+ {
315
+ return _loadQueue.Count + _unloadQueue.Count;
316
+ }
243
317
  }
244
318
  }
@@ -1,5 +1,4 @@
1
1
  using System.Collections.Generic;
2
- using System.Linq;
3
2
  using UnityEngine;
4
3
  using jeanf.EventSystem;
5
4
  using jeanf.propertyDrawer;
@@ -14,36 +13,57 @@ namespace jeanf.scenemanagement
14
13
  public bool isDebug = false;
15
14
  private SceneLoader _sceneLoader;
16
15
  public List<Region> ListOfRegions;
16
+
17
17
  private Dictionary<string, Zone> _zoneDictionary = new Dictionary<string, Zone>();
18
18
  private Dictionary<string, Region> _regionDictionary = new Dictionary<string, Region>();
19
19
  private Dictionary<string, Region> _regionDictionaryPerZone = new Dictionary<string, Region>();
20
- private Dictionary<Collider, Region> _regionDictionaryPerCollider = new Dictionary<Collider, Region>();
21
20
  private Dictionary<string, List<SceneReference>> _dependenciesPerRegion = new Dictionary<string, List<SceneReference>>();
21
+ private Dictionary<string, List<string>> _compiledSceneLists = new Dictionary<string, List<string>>();
22
+ private HashSet<string> _landingZoneIds = new HashSet<string>();
22
23
 
23
24
  private List<Region> _activeRegions = new List<Region>();
24
- private List<GameObject> _activeZones = new List<GameObject>();
25
+ private bool _mappingInitialized = false;
26
+
27
+ private List<string> _tempSceneNames = new List<string>();
28
+ private List<Region> _tempRegionsToRemove = new List<Region>();
25
29
 
26
30
  [SerializeField] private StringEventChannelSO regionChangeRequestChannel;
27
31
  [SerializeField] private SendTeleportTarget sendTeleportTarget;
28
32
 
29
33
  [ReadOnly] [SerializeField] private Zone _currentPlayerZone;
34
+ [ReadOnly] [SerializeField] private Region _currentPlayerRegion;
35
+
30
36
  private static WorldManager Instance;
37
+ private static bool _isRegionTransitioning = false;
38
+
31
39
  public static Zone CurrentPlayerZone
32
40
  {
33
- get => Instance._currentPlayerZone;
34
- private set => Instance._currentPlayerZone = value;
41
+ get => Instance?._currentPlayerZone;
42
+ private set
43
+ {
44
+ if (Instance != null)
45
+ {
46
+ Instance._currentPlayerZone = value;
47
+ }
48
+ }
35
49
  }
36
50
 
37
- [ReadOnly] [SerializeField] private Region _currentPlayerRegion;
38
51
  public static Region CurrentPlayerRegion
39
52
  {
40
- get => Instance._currentPlayerRegion;
41
- private set => Instance._currentPlayerRegion = value;
53
+ get => Instance?._currentPlayerRegion;
54
+ private set
55
+ {
56
+ if (Instance != null)
57
+ {
58
+ Instance._currentPlayerRegion = value;
59
+ }
60
+ }
42
61
  }
43
62
 
63
+ public static bool IsRegionTransitioning => _isRegionTransitioning;
64
+
44
65
  public delegate void SendId(string newRegionID);
45
66
  public static SendId RequestRegionChange;
46
-
47
67
  public static SendId PublishCurrentRegionId;
48
68
  public static SendId PublishCurrentZoneId;
49
69
 
@@ -54,7 +74,8 @@ namespace jeanf.scenemanagement
54
74
  public static BroadcastAppList _broadcastAppList;
55
75
 
56
76
  private bool hasGameBeenInitialized = false;
57
-
77
+ private string _lastNotifiedZone = "";
78
+ private string _lastNotifiedRegion = "";
58
79
 
59
80
  private void Awake()
60
81
  {
@@ -71,7 +92,6 @@ namespace jeanf.scenemanagement
71
92
  {
72
93
  regionChangeRequestChannel.OnEventRaised += OnRegionChange;
73
94
  RequestRegionChange += OnRegionChange;
74
-
75
95
  ResetWorld += Init;
76
96
  ScenarioManager.OnZoneOverridesChanged += OnZoneOverridesChanged;
77
97
  }
@@ -80,112 +100,214 @@ namespace jeanf.scenemanagement
80
100
  {
81
101
  regionChangeRequestChannel.OnEventRaised -= OnRegionChange;
82
102
  RequestRegionChange -= OnRegionChange;
83
-
84
103
  ResetWorld -= Init;
85
104
  ScenarioManager.OnZoneOverridesChanged -= OnZoneOverridesChanged;
86
105
  }
87
106
 
88
-
89
107
  private void Init()
90
108
  {
91
- if(isDebug) Debug.Log($"[WorldManager] World reset.");
92
109
  hasGameBeenInitialized = false;
110
+ _currentPlayerZone = null;
111
+ _currentPlayerRegion = null;
112
+ _lastNotifiedZone = "";
113
+ _lastNotifiedRegion = "";
114
+ _isRegionTransitioning = false;
115
+
116
+ ClearAllMappings();
117
+ BuildDataMappings();
118
+ }
119
+
120
+ private void ClearAllMappings()
121
+ {
122
+ _zoneDictionary.Clear();
123
+ _regionDictionary.Clear();
124
+ _regionDictionaryPerZone.Clear();
125
+ _dependenciesPerRegion.Clear();
126
+ _compiledSceneLists.Clear();
127
+ _landingZoneIds.Clear();
128
+ _activeRegions.Clear();
129
+ _tempSceneNames.Clear();
130
+ _tempRegionsToRemove.Clear();
131
+ _mappingInitialized = false;
132
+ }
133
+
134
+ private void BuildDataMappings()
135
+ {
136
+ if (_mappingInitialized) return;
137
+
138
+ var regionCount = ListOfRegions?.Count ?? 0;
139
+ if (regionCount == 0) return;
140
+
93
141
  foreach (var region in ListOfRegions)
94
142
  {
95
- // build region Dictionary
96
- if(isDebug) Debug.Log($"[WorldManager] Adding region with id: {region.id.id} to the region dictionary.");
97
- _regionDictionary.TryAdd(region.id, region);
143
+ if (region == null) continue;
144
+ if (!_regionDictionary.TryAdd(region.id, region)) continue;
145
+
146
+ _dependenciesPerRegion.TryAdd(region.id, region.dependenciesInThisRegion);
147
+ PrecompileSceneList(region);
98
148
 
99
- // build scenario Dictionary
100
- foreach (var scenario in region.scenariosInThisRegion)
149
+ if (region.scenariosInThisRegion != null)
101
150
  {
102
- if(isDebug) Debug.Log($"[WorldManager] Adding scenario with id: {scenario.id.id} to the scenario dictionary.");
103
- ScenarioManager.ScenarioDictionary.TryAdd(scenario.id, scenario);
151
+ foreach (var scenario in region.scenariosInThisRegion)
152
+ {
153
+ if (scenario != null)
154
+ {
155
+ ScenarioManager.ScenarioDictionary.TryAdd(scenario.id, scenario);
156
+ }
157
+ }
104
158
  }
105
159
 
106
- // build zoneData Dictionary
107
- foreach (var zone in region.zonesInThisRegion)
160
+ if (region.zonesInThisRegion != null)
108
161
  {
109
- if(isDebug) Debug.Log($"[WorldManager] Adding zone with id: {zone.id} to the zone dictionary.");
110
- _zoneDictionary.TryAdd(zone.id, zone);
111
- _regionDictionaryPerZone.TryAdd(zone.id, region);
112
- }
113
-
114
- // build dependency Dictionary
115
- if(isDebug) Debug.Log($"[WorldManager] Adding list of dependencies for the region with id: {region.id.id} to the dependency dictionary.");
116
- _dependenciesPerRegion.TryAdd(region.id, region.dependenciesInThisRegion);
162
+ foreach (var zone in region.zonesInThisRegion)
163
+ {
164
+ if (zone != null)
165
+ {
166
+ _zoneDictionary.TryAdd(zone.id, zone);
167
+ _regionDictionaryPerZone.TryAdd(zone.id, region);
168
+ }
169
+ }
170
+ }
171
+ }
172
+
173
+ BuildLandingZoneCache();
174
+ _mappingInitialized = true;
175
+ }
176
+
177
+ private void PrecompileSceneList(Region region)
178
+ {
179
+ var sceneNames = new List<string>(region.dependenciesInThisRegion.Count);
180
+ foreach (var dependency in region.dependenciesInThisRegion)
181
+ {
182
+ sceneNames.Add(dependency.SceneName);
183
+ }
184
+ _compiledSceneLists[region.id] = sceneNames;
185
+ }
186
+
187
+ private void BuildLandingZoneCache()
188
+ {
189
+ var connectivity = FindObjectOfType<RegionConnectivityAuthoring>();
190
+ if (connectivity?.regionConnectivity?.landingZones == null) return;
191
+
192
+ foreach (var landing in connectivity.regionConnectivity.landingZones)
193
+ {
194
+ if (landing?.landingZone != null)
195
+ {
196
+ _landingZoneIds.Add(landing.landingZone.id);
197
+ }
117
198
  }
118
199
  }
119
200
 
120
201
  public static void NotifyZoneChangeFromECS(string zoneId)
121
202
  {
122
- if (Instance != null)
203
+ if (Instance != null && !_isRegionTransitioning)
123
204
  {
124
205
  Instance.OnZoneChangedFromECS(zoneId);
125
206
  }
126
207
  }
208
+
209
+ public static void NotifyRegionChangeFromECS(string regionId)
210
+ {
211
+ if (Instance != null && !_isRegionTransitioning)
212
+ {
213
+ Instance.OnRegionChangedFromECS(regionId);
214
+ }
215
+ }
127
216
 
128
217
  private void OnZoneChangedFromECS(string zoneId)
129
218
  {
219
+ if (string.IsNullOrEmpty(zoneId) || _lastNotifiedZone == zoneId) return;
220
+
130
221
  if (!_zoneDictionary.TryGetValue(zoneId, out var zone)) return;
131
-
132
- CurrentPlayerZone = zone;
133
- PublishCurrentZoneId?.Invoke(zone.id.id);
222
+
223
+ _lastNotifiedZone = zoneId;
224
+ _currentPlayerZone = zone;
225
+
226
+ PublishCurrentZoneId?.Invoke(zone.id);
134
227
  PublishAppList(zone);
135
-
136
- var newRegion = _regionDictionaryPerZone[zone.id.id];
137
- if(newRegion != CurrentPlayerRegion) OnRegionChange(newRegion);
138
228
  }
229
+
230
+ private void OnRegionChangedFromECS(string regionId)
231
+ {
232
+ if (string.IsNullOrEmpty(regionId) || _lastNotifiedRegion == regionId) return;
233
+
234
+ if (!_regionDictionary.TryGetValue(regionId, out var region)) return;
235
+
236
+ _lastNotifiedRegion = regionId;
237
+ _currentPlayerRegion = region;
238
+
239
+ OnRegionChange(region);
240
+ }
241
+
139
242
  private void OnZoneOverridesChanged(string zoneId)
140
243
  {
141
- // Only update if we're in the affected zone
142
- if (CurrentPlayerZone.id == zoneId)
244
+ if (CurrentPlayerZone != null && CurrentPlayerZone.id == zoneId)
143
245
  {
144
246
  PublishAppList(CurrentPlayerZone);
145
247
  }
146
248
  }
147
249
 
148
- private void SetCurrentZoneAndRegion(GameObject gameObject, Zone zone)
149
- {
150
- if (!gameObject.CompareTag("Player")) return;
151
- CurrentPlayerZone = zone;
152
- PublishCurrentZoneId?.Invoke(zone.id);
153
- PublishAppList(zone);
154
- var newRegion = _regionDictionaryPerZone[zone.id];
155
- if(newRegion != CurrentPlayerRegion) OnRegionChange(newRegion);
156
- }
157
-
158
250
  private void OnRegionChange(string newRegionID)
159
251
  {
160
252
  if (!_regionDictionary.TryGetValue(newRegionID, out var region)) return;
161
253
  OnRegionChange(region);
162
254
  }
163
255
 
164
- // ReSharper disable Unity.PerformanceAnalysis
165
256
  private void OnRegionChange(Region region)
166
257
  {
167
- CurrentPlayerRegion = region;
168
- PublishCurrentRegionId?.Invoke(CurrentPlayerRegion.id);
258
+ _isRegionTransitioning = true;
259
+
260
+ _currentPlayerRegion = region;
261
+ _lastNotifiedRegion = region.id;
262
+
263
+ PublishCurrentRegionId?.Invoke(_currentPlayerRegion.id);
169
264
 
170
- var currentActiveRegion = _activeRegions;
171
- var regionsToRemove = currentActiveRegion.Select(RequestUnLoadForObsoleteRegion).ToList();
265
+ _tempRegionsToRemove.Clear();
266
+ foreach (var activeRegion in _activeRegions)
267
+ {
268
+ var removedRegion = RequestUnLoadForObsoleteRegion(activeRegion);
269
+ _tempRegionsToRemove.Add(removedRegion);
270
+ }
172
271
 
173
- foreach (var r in regionsToRemove)
272
+ foreach (var r in _tempRegionsToRemove)
174
273
  {
175
274
  _activeRegions.Remove(r);
176
275
  }
177
276
 
178
277
  RequestLoadForRegionDependencies(region);
179
278
 
180
- // set teleporting position
181
279
  var spawnPos = SetTeleportTarget(region, hasGameBeenInitialized);
182
- sendTeleportTarget.transform.position = spawnPos.position;
183
- sendTeleportTarget.transform.rotation = Quaternion.Euler(spawnPos.rotation);
184
- // teleport!
185
- sendTeleportTarget.Teleport();
186
280
 
187
- Debug.Log($"[WorldManager] Current region: {region.levelName}.");
281
+ if (sendTeleportTarget != null)
282
+ {
283
+ sendTeleportTarget.transform.position = spawnPos.position;
284
+ sendTeleportTarget.transform.rotation = Quaternion.Euler(spawnPos.rotation);
285
+ sendTeleportTarget.Teleport();
286
+ }
287
+
188
288
  _activeRegions.Add(region);
289
+
290
+ StartCoroutine(CompleteRegionTransition());
291
+ }
292
+
293
+ private System.Collections.IEnumerator CompleteRegionTransition()
294
+ {
295
+ yield return new WaitForEndOfFrame();
296
+ yield return new WaitForEndOfFrame();
297
+
298
+ _isRegionTransitioning = false;
299
+
300
+ if (_currentPlayerRegion?.zonesInThisRegion != null && _currentPlayerRegion.zonesInThisRegion.Count > 0)
301
+ {
302
+ var firstZone = _currentPlayerRegion.zonesInThisRegion[0];
303
+ if (firstZone != null)
304
+ {
305
+ _currentPlayerZone = firstZone;
306
+ _lastNotifiedZone = firstZone.id;
307
+ PublishCurrentZoneId?.Invoke(firstZone.id);
308
+ PublishAppList(firstZone);
309
+ }
310
+ }
189
311
  }
190
312
 
191
313
  private SpawnPos SetTeleportTarget(Region region, bool hasRegionBeenInitialized)
@@ -203,39 +325,59 @@ namespace jeanf.scenemanagement
203
325
  private void PublishAppList(Zone zone)
204
326
  {
205
327
  var listToBroadcast = zone.DefaultAppsInZone;
206
- if(isDebug) Debug.Log($"[WorldManager] Default list for zone [{zone.name}] : [{string.Join(", ", listToBroadcast)}]");
207
- // check if for this zone there is no override
208
328
  if (ScenarioManager.activeOverridesPerZone.TryGetValue(zone.id, out var value))
209
329
  {
210
- // if yes, send override list
211
330
  listToBroadcast = value;
212
- if(isDebug) Debug.Log($"[WorldManager] List override found for zone [{zone.name}] : [{string.Join(", ", listToBroadcast)}]");
213
331
  }
214
332
 
215
- // broadcast list
216
333
  _broadcastAppList?.Invoke(listToBroadcast);
217
334
  }
218
335
 
219
336
  private void RequestLoadForRegionDependencies(Region region)
220
337
  {
221
- if ( region.dependenciesInThisRegion.Count <= 0 ) return;
222
- foreach (var scene in CompileSceneList(region))
338
+ if (!_compiledSceneLists.TryGetValue(region.id, out var sceneNames) || sceneNames.Count <= 0) return;
339
+
340
+ foreach (var sceneName in sceneNames)
223
341
  {
224
- _sceneLoader.LoadSceneRequest(scene);
342
+ _sceneLoader.LoadSceneRequest(sceneName);
225
343
  }
226
344
  }
345
+
227
346
  private Region RequestUnLoadForObsoleteRegion(Region region)
228
347
  {
229
- foreach (var scene in CompileSceneList(region))
348
+ if (_compiledSceneLists.TryGetValue(region.id, out var sceneNames))
230
349
  {
231
- _sceneLoader.UnLoadSceneRequest(scene);
350
+ foreach (var sceneName in sceneNames)
351
+ {
352
+ _sceneLoader.UnLoadSceneRequest(sceneName);
353
+ }
232
354
  }
233
355
  return region;
234
356
  }
235
-
236
- private static List<string> CompileSceneList(Region region)
357
+
358
+ public bool IsLandingZone(string zoneId)
359
+ {
360
+ return _landingZoneIds.Contains(zoneId);
361
+ }
362
+
363
+ public HashSet<string> GetLandingZoneIds()
364
+ {
365
+ return _landingZoneIds;
366
+ }
367
+
368
+ public static Dictionary<string, Zone> GetZoneDictionary()
369
+ {
370
+ return Instance?._zoneDictionary;
371
+ }
372
+
373
+ public static Dictionary<string, Region> GetRegionDictionary()
374
+ {
375
+ return Instance?._regionDictionary;
376
+ }
377
+
378
+ public static Dictionary<string, Region> GetRegionDictionaryPerZone()
237
379
  {
238
- return region.dependenciesInThisRegion.Select(dependency => dependency.SceneName).ToList();
380
+ return Instance?._regionDictionaryPerZone;
239
381
  }
240
382
  }
241
383
  }