Skip to content

Commit c70235e

Browse files
authored
Merge pull request #15 from mygamedevtools/fix/transition-errors
[Fix] Correct scene assignment from load/unload operations
2 parents 6eea07a + a16d139 commit c70235e

File tree

13 files changed

+185
-40
lines changed

13 files changed

+185
-40
lines changed

Packages/mygamedevtools-scene-loader/Runtime/Interfaces/ISceneManager.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,9 @@ public interface ISceneManager
9898
/// <returns>A loaded scene with the given <paramref name="name"/>.</returns>
9999
Scene GetLoadedSceneByName(string name);
100100
}
101+
102+
internal interface ISceneManagerReporter
103+
{
104+
bool IsUnloadingScenes { get; }
105+
}
101106
}

Packages/mygamedevtools-scene-loader/Runtime/Managers/SceneManager.cs

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,23 @@
1212
#endif
1313
using System;
1414
using System.Collections.Generic;
15+
using System.Runtime.CompilerServices;
1516
using System.Threading.Tasks;
1617
using UnityEngine;
1718
using UnityEngine.SceneManagement;
1819
using UnitySceneManager = UnityEngine.SceneManagement.SceneManager;
1920

21+
[assembly: InternalsVisibleTo("MyGameDevTools.SceneLoading.Tests")]
22+
2023
namespace MyGameDevTools.SceneLoading
2124
{
22-
public class SceneManager : ISceneManager
25+
public class SceneManager : ISceneManager, ISceneManagerReporter
2326
{
2427
public event Action<Scene, Scene> ActiveSceneChanged;
2528
public event Action<Scene> SceneUnloaded;
2629
public event Action<Scene> SceneLoaded;
2730

31+
public bool IsUnloadingScenes => _unloadingScenes.Count > 0;
2832
public int SceneCount => _loadedScenes.Count;
2933

3034
readonly List<Scene> _unloadingScenes = new List<Scene>();
@@ -54,7 +58,7 @@ public Scene GetLastLoadedScene()
5458
return default;
5559

5660
for (int i = SceneCount - 1; i >= 0; i--)
57-
if (!_unloadingScenes.Contains(_loadedScenes[i]))
61+
if (!_unloadingScenes.Contains(_loadedScenes[i]) && _loadedScenes[i].isLoaded)
5862
return _loadedScenes[i];
5963

6064
return default;
@@ -73,9 +77,6 @@ public Scene GetLoadedSceneByName(string name)
7377
public async ValueTask<Scene> LoadSceneAsync(ILoadSceneInfo sceneInfo, bool setActive = false, IProgress<float> progress = null)
7478
{
7579
var operation = GetLoadSceneOperation(sceneInfo);
76-
Scene loadedScene = default;
77-
78-
UnitySceneManager.sceneLoaded += registerLoadedScene;
7980

8081
#if USE_UNITASK
8182
await operation.ToUniTask(progress);
@@ -87,7 +88,7 @@ public async ValueTask<Scene> LoadSceneAsync(ILoadSceneInfo sceneInfo, bool setA
8788
}
8889
#endif
8990

90-
UnitySceneManager.sceneLoaded -= registerLoadedScene;
91+
var loadedScene = GetLastUnityLoadedSceneByInfo(sceneInfo);
9192

9293
_loadedScenes.Add(loadedScene);
9394
SceneLoaded?.Invoke(loadedScene);
@@ -96,22 +97,17 @@ public async ValueTask<Scene> LoadSceneAsync(ILoadSceneInfo sceneInfo, bool setA
9697
SetActiveScene(loadedScene);
9798

9899
return loadedScene;
99-
100-
void registerLoadedScene(Scene scene, LoadSceneMode loadSceneMode)
101-
{
102-
if (sceneInfo.IsReferenceToScene(scene))
103-
loadedScene = scene;
104-
}
105100
}
106101

107102
public async ValueTask<Scene> UnloadSceneAsync(ILoadSceneInfo sceneInfo)
108103
{
109-
var scene = GetLastLoadedSceneByInfo(sceneInfo);
110-
if (!_loadedScenes.Contains(scene))
111-
throw new InvalidOperationException($"Cannot unload the scene \"{scene.name}\" that has not been loaded through this {GetType().Name}.");
104+
var scene = GetLastSceneByInfo(sceneInfo);
112105
if (_unloadingScenes.Contains(scene))
113106
return await WaitForSceneUnload(scene);
107+
if (!_loadedScenes.Contains(scene))
108+
throw new InvalidOperationException($"Cannot unload the scene \"{scene.name}\" that has not been loaded through this {GetType().Name}.");
114109

110+
_loadedScenes.Remove(scene);
115111
_unloadingScenes.Add(scene);
116112
var operation = GetUnloadSceneOperation(sceneInfo);
117113
#if USE_UNITASK
@@ -122,7 +118,6 @@ public async ValueTask<Scene> UnloadSceneAsync(ILoadSceneInfo sceneInfo)
122118
#endif
123119

124120
_unloadingScenes.Remove(scene);
125-
_loadedScenes.Remove(scene);
126121
if (_activeScene == scene)
127122
SetActiveScene(GetLastLoadedScene());
128123

@@ -166,16 +161,37 @@ AsyncOperation GetUnloadSceneOperation(ILoadSceneInfo sceneInfo)
166161
throw new Exception($"Unexpected {nameof(ILoadSceneInfo.Reference)} type.");
167162
}
168163

169-
Scene GetLastLoadedSceneByInfo(ILoadSceneInfo sceneInfo)
164+
Scene GetLastSceneByInfo(ILoadSceneInfo sceneInfo)
170165
{
171166
var sceneCount = SceneCount;
172-
for (int i = sceneCount - 1; i >= 0; i--)
167+
int i;
168+
for (i = sceneCount - 1; i >= 0; i--)
173169
{
174170
var scene = _loadedScenes[i];
175171
if (sceneInfo.IsReferenceToScene(scene))
176172
return scene;
177173
}
178-
throw new ArgumentException($"Could not find any loaded scene with the provided ILoadSceneInfo: {sceneInfo.Reference}");
174+
175+
sceneCount = _unloadingScenes.Count;
176+
for (i = 0; i < sceneCount; i++)
177+
{
178+
var scene = _unloadingScenes[i];
179+
if (sceneInfo.IsReferenceToScene(scene))
180+
return scene;
181+
}
182+
throw new ArgumentException($"Could not find any scene with the provided ILoadSceneInfo: {sceneInfo}");
183+
}
184+
185+
Scene GetLastUnityLoadedSceneByInfo(ILoadSceneInfo sceneInfo)
186+
{
187+
var sceneCount = UnitySceneManager.sceneCount;
188+
for (int i = sceneCount - 1; i >= 0; i--)
189+
{
190+
var scene = UnitySceneManager.GetSceneAt(i);
191+
if (scene.isLoaded && sceneInfo.IsReferenceToScene(scene))
192+
return scene;
193+
}
194+
throw new ArgumentException($"Could not find any loaded scene in the Unity Scene Manager with the provided ILoadSceneInfo: {sceneInfo}");
179195
}
180196
}
181197
}

Packages/mygamedevtools-scene-loader/Runtime/Managers/SceneManagerAddressable.cs

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,13 @@
2222

2323
namespace MyGameDevTools.SceneLoading
2424
{
25-
public class SceneManagerAddressable : ISceneManager
25+
public class SceneManagerAddressable : ISceneManager, ISceneManagerReporter
2626
{
2727
public event Action<Scene, Scene> ActiveSceneChanged;
2828
public event Action<Scene> SceneUnloaded;
2929
public event Action<Scene> SceneLoaded;
3030

31+
public bool IsUnloadingScenes => _unloadingScenes.Count > 0;
3132
public int SceneCount => _loadedScenes.Count;
3233

3334
readonly List<SceneInstance> _unloadingScenes = new List<SceneInstance>();
@@ -105,7 +106,7 @@ public async ValueTask<Scene> LoadSceneAsync(ILoadSceneInfo sceneInfo, bool setA
105106

106107
public async ValueTask<Scene> UnloadSceneAsync(ILoadSceneInfo sceneInfo)
107108
{
108-
var sceneInstance = GetLastLoadedSceneByInfo(sceneInfo);
109+
var sceneInstance = GetLastSceneByInfo(sceneInfo);
109110
if (!_loadedScenes.Contains(sceneInstance))
110111
throw new InvalidOperationException($"Cannot unload the scene \"{sceneInstance.Scene.name}\" that has not been loaded through this {GetType().Name}.");
111112
if (_unloadingScenes.Contains(sceneInstance))
@@ -116,23 +117,27 @@ public async ValueTask<Scene> UnloadSceneAsync(ILoadSceneInfo sceneInfo)
116117
#if USE_UNITASK
117118
await operation.ToUniTask();
118119
#else
119-
while (!operation.IsDone)
120+
while (operation.IsValid() && !operation.IsDone)
120121
await Task.Yield();
121122
#endif
122123

123-
if (operation.Status == AsyncOperationStatus.Failed)
124-
throw operation.OperationException;
124+
Scene unloadedScene;
125125

126-
var loadedScene = operation.Result.Scene;
126+
if (!operation.IsValid())
127+
unloadedScene = sceneInstance.Scene;
128+
else if (operation.Status == AsyncOperationStatus.Failed)
129+
throw operation.OperationException;
130+
else
131+
unloadedScene = operation.Result.Scene;
127132

128133
_unloadingScenes.Remove(sceneInstance);
129134
_loadedScenes.Remove(sceneInstance);
130135
if (_activeSceneInstance.Scene == sceneInstance.Scene)
131136
SetActiveScene(GetLastLoadedScene());
132137

133-
SceneUnloaded?.Invoke(loadedScene);
138+
SceneUnloaded?.Invoke(unloadedScene);
134139

135-
return loadedScene;
140+
return unloadedScene;
136141
}
137142

138143
async ValueTask<Scene> WaitForSceneUnload(SceneInstance sceneInstance)
@@ -160,16 +165,25 @@ AsyncOperationHandle<SceneInstance> GetLoadSceneOperation(ILoadSceneInfo sceneIn
160165
throw new Exception($"Unexpected {nameof(ILoadSceneInfo.Reference)} type.");
161166
}
162167

163-
SceneInstance GetLastLoadedSceneByInfo(ILoadSceneInfo sceneInfo)
168+
SceneInstance GetLastSceneByInfo(ILoadSceneInfo sceneInfo)
164169
{
165170
var sceneCount = SceneCount;
166-
for (int i = sceneCount - 1; i >= 0; i--)
171+
int i;
172+
for (i = sceneCount - 1; i >= 0; i--)
167173
{
168174
var sceneInstance = _loadedScenes[i];
169175
if (sceneInfo.IsReferenceToScene(sceneInstance.Scene))
170176
return sceneInstance;
171177
}
172-
throw new ArgumentException($"Could not find any loaded scene with the provided ILoadSceneInfo: {sceneInfo.Reference}");
178+
179+
sceneCount = _unloadingScenes.Count;
180+
for (i = 0; i < sceneCount; i++)
181+
{
182+
var sceneInstance = _unloadingScenes[i];
183+
if (sceneInfo.IsReferenceToScene(sceneInstance.Scene))
184+
return sceneInstance;
185+
}
186+
throw new ArgumentException($"Could not find any loaded scene with the provided ILoadSceneInfo: {sceneInfo}");
173187
}
174188

175189
bool ValidateAssetReference(object reference)

Packages/mygamedevtools-scene-loader/Runtime/SceneLoaders/SceneLoaderAsync.cs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
*/
66

77
using System;
8+
using System.Linq;
89
using System.Threading.Tasks;
10+
using UnityEngine;
911
using UnityEngine.SceneManagement;
1012
using Object = UnityEngine.Object;
1113

@@ -45,10 +47,13 @@ async ValueTask<Scene> TransitionDirectlyAsync(ILoadSceneInfo loadSceneInfo, Sce
4547
async ValueTask<Scene> TransitionWithIntermediateAsync(ILoadSceneInfo targetSceneInfo, ILoadSceneInfo intermediateSceneInfo, Scene externalOriginScene)
4648
{
4749
var externalOrigin = externalOriginScene.IsValid();
50+
51+
var loadingScene = await _manager.LoadSceneAsync(intermediateSceneInfo);
52+
intermediateSceneInfo = new LoadSceneInfoScene(loadingScene);
53+
4854
var currentScene = externalOrigin ? externalOriginScene : _manager.GetActiveScene();
49-
await _manager.LoadSceneAsync(intermediateSceneInfo);
5055

51-
var loadingBehavior = Object.FindObjectOfType<LoadingBehavior>();
56+
var loadingBehavior = Object.FindObjectsOfType<LoadingBehavior>().FirstOrDefault(l => l.gameObject.scene == loadingScene);
5257
return loadingBehavior
5358
? await TransitionWithIntermediateLoadingAsync(targetSceneInfo, intermediateSceneInfo, loadingBehavior, currentScene, externalOrigin)
5459
: await TransitionWithIntermediateNoLoadingAsync(targetSceneInfo, intermediateSceneInfo, currentScene, externalOrigin);
@@ -60,6 +65,9 @@ async ValueTask<Scene> TransitionWithIntermediateLoadingAsync(ILoadSceneInfo tar
6065
while (progress.State != LoadingState.Loading)
6166
await Task.Yield();
6267

68+
if (!externalOrigin)
69+
currentScene = _manager.GetActiveScene();
70+
6371
await UnloadCurrentScene(currentScene, externalOrigin);
6472

6573
var loadedScene = await _manager.LoadSceneAsync(targetSceneInfo, true, progress);
@@ -94,5 +102,10 @@ async ValueTask UnloadCurrentScene(Scene currentScene, bool externalOrigin)
94102
else
95103
await _manager.UnloadSceneAsync(new LoadSceneInfoScene(currentScene));
96104
}
105+
106+
public override string ToString()
107+
{
108+
return $"Scene Loader [Async] with {_manager.GetType().Name}";
109+
}
97110
}
98111
}

Packages/mygamedevtools-scene-loader/Runtime/SceneLoaders/SceneLoaderCoroutine.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Cysharp.Threading.Tasks;
88
using System;
99
using System.Collections;
10+
using System.Linq;
1011
using UnityEngine;
1112
using UnityEngine.SceneManagement;
1213
using Object = UnityEngine.Object;
@@ -57,10 +58,15 @@ IEnumerator TransitionDirectlyRoutine(ILoadSceneInfo targetSceneInfo, Scene exte
5758
IEnumerator TransitionWithIntermediateRoutine(ILoadSceneInfo targetSceneInfo, ILoadSceneInfo intermediateSceneInfo, Scene externalOriginScene)
5859
{
5960
var externalOrigin = externalOriginScene.IsValid();
61+
62+
var task = _manager.LoadSceneAsync(intermediateSceneInfo).AsTask();
63+
yield return new WaitTask(task);
64+
var loadingScene = task.Result;
65+
intermediateSceneInfo = new LoadSceneInfoScene(loadingScene);
66+
6067
var currentScene = externalOrigin ? externalOriginScene : _manager.GetActiveScene();
61-
yield return new WaitTask(_manager.LoadSceneAsync(intermediateSceneInfo).AsTask());
6268

63-
var loadingBehavior = Object.FindObjectOfType<LoadingBehavior>();
69+
var loadingBehavior = Object.FindObjectsOfType<LoadingBehavior>().FirstOrDefault(l => l.gameObject.scene == loadingScene);
6470
yield return loadingBehavior
6571
? TransitionWithIntermediateLoadingAsync(targetSceneInfo, intermediateSceneInfo, loadingBehavior, currentScene, externalOrigin)
6672
: TransitionWithIntermediateNoLoadingAsync(targetSceneInfo, intermediateSceneInfo, currentScene, externalOrigin);
@@ -71,6 +77,9 @@ IEnumerator TransitionWithIntermediateLoadingAsync(ILoadSceneInfo targetSceneInf
7177
var progress = loadingBehavior.Progress;
7278
yield return new WaitUntil(() => progress.State == LoadingState.Loading);
7379

80+
if (!externalOrigin)
81+
currentScene = _manager.GetActiveScene();
82+
7483
yield return UnloadCurrentScene(currentScene, externalOrigin);
7584

7685
yield return new WaitTask(_manager.LoadSceneAsync(targetSceneInfo, true, progress).AsTask());
@@ -98,5 +107,10 @@ IEnumerator UnloadCurrentScene(Scene currentScene, bool externalOrigin)
98107
else
99108
yield return UnloadRoutine(new LoadSceneInfoScene(currentScene));
100109
}
110+
111+
public override string ToString()
112+
{
113+
return $"Scene Loader [Coroutine] with {_manager.GetType().Name}";
114+
}
101115
}
102116
}

Packages/mygamedevtools-scene-loader/Runtime/SceneLoaders/SceneLoaderUniTask.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
using Cysharp.Threading.Tasks;
99
using System;
10+
using System.Linq;
1011
using UnityEngine.SceneManagement;
1112
using Object = UnityEngine.Object;
1213

@@ -46,10 +47,13 @@ async UniTask<Scene> TransitionDirectlyAsync(ILoadSceneInfo loadSceneInfo, Scene
4647
async UniTask<Scene> TransitionWithIntermediateAsync(ILoadSceneInfo targetSceneInfo, ILoadSceneInfo intermediateSceneInfo, Scene externalOriginScene)
4748
{
4849
var externalOrigin = externalOriginScene.IsValid();
50+
51+
var loadingScene = await _manager.LoadSceneAsync(intermediateSceneInfo);
52+
intermediateSceneInfo = new LoadSceneInfoScene(loadingScene);
53+
4954
var currentScene = externalOrigin ? externalOriginScene : _manager.GetActiveScene();
50-
await _manager.LoadSceneAsync(intermediateSceneInfo);
5155

52-
var loadingBehavior = Object.FindObjectOfType<LoadingBehavior>();
56+
var loadingBehavior = Object.FindObjectsOfType<LoadingBehavior>().FirstOrDefault(l => l.gameObject.scene == loadingScene);
5357
return loadingBehavior
5458
? await TransitionWithIntermediateLoadingAsync(targetSceneInfo, intermediateSceneInfo, loadingBehavior, currentScene, externalOrigin)
5559
: await TransitionWithIntermediateNoLoadingAsync(targetSceneInfo, intermediateSceneInfo, currentScene, externalOrigin);
@@ -60,6 +64,9 @@ async UniTask<Scene> TransitionWithIntermediateLoadingAsync(ILoadSceneInfo targe
6064
var progress = loadingBehavior.Progress;
6165
await UniTask.WaitUntil(() => progress.State == LoadingState.Loading);
6266

67+
if (!externalOrigin)
68+
currentScene = _manager.GetActiveScene();
69+
6370
await UnloadCurrentScene(currentScene, externalOrigin);
6471

6572
var loadedScene = await _manager.LoadSceneAsync(targetSceneInfo, true, progress);
@@ -89,6 +96,11 @@ async UniTask UnloadCurrentScene(Scene currentScene, bool externalOrigin)
8996
else
9097
await _manager.UnloadSceneAsync(new LoadSceneInfoScene(currentScene));
9198
}
99+
100+
public override string ToString()
101+
{
102+
return $"Scene Loader [UniTask] with {_manager.GetType().Name}";
103+
}
92104
}
93105
}
94106
#endif

Packages/mygamedevtools-scene-loader/Runtime/Structs/LoadSceneInfoAssetReference.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ public bool IsReferenceToScene(Scene scene)
3333
var sceneInstance = _assetReference.OperationHandle.Convert<SceneInstance>().Result;
3434
return sceneInstance.Scene == scene;
3535
}
36+
37+
public override string ToString()
38+
{
39+
return $"Scene with asset reference {_assetReference}";
40+
}
3641
}
3742
}
3843
#endif

Packages/mygamedevtools-scene-loader/Runtime/Structs/LoadSceneInfoIndex.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,10 @@ public LoadSceneInfoIndex(int buildIndex)
3131
}
3232

3333
public bool IsReferenceToScene(Scene scene) => scene.buildIndex == _buildIndex;
34+
35+
public override string ToString()
36+
{
37+
return $"Scene with index [{_buildIndex}]";
38+
}
3439
}
3540
}

0 commit comments

Comments
 (0)