Skip to content

Commit 64205fb

Browse files
committed
feat: add transitions from external scenes
1 parent f33c852 commit 64205fb

File tree

6 files changed

+171
-91
lines changed

6 files changed

+171
-91
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
* Created on: 7/16/2022 (en-US)
55
*/
66

7+
using UnityEngine.SceneManagement;
8+
79
namespace MyGameDevTools.SceneLoading
810
{
911
/// <summary>
@@ -36,7 +38,7 @@ public interface ISceneLoader
3638
/// A reference to the scene that's going to be loaded as the transition intermediate (as a loading scene).
3739
/// If null, the transition will not have an intermediate loading scene.
3840
/// </param>
39-
void TransitionToScene(ILoadSceneInfo targetSceneInfo, ILoadSceneInfo intermediateSceneInfo = null);
41+
void TransitionToScene(ILoadSceneInfo targetSceneInfo, ILoadSceneInfo intermediateSceneInfo = null, Scene externalOriginScene = default);
4042

4143
/// <summary>
4244
/// Unloads the given scene from the current scene stack.

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
*/
66

77
using System;
8+
using UnityEngine.SceneManagement;
89

910
namespace MyGameDevTools.SceneLoading
1011
{
1112
/// <summary>
1213
/// Interface to standardize async scene loading operations.
13-
/// <typeparamref name="TAsync"/> can be a <see cref="UnityEngine.Coroutine"/> or an awaitable type that returns <see cref="UnityEngine.SceneManagement.Scene"/>, such as
14+
/// <typeparamref name="TAsync"/> can be a <see cref="UnityEngine.Coroutine"/> or an awaitable type that returns <see cref="Scene"/>, such as
1415
/// <see cref="System.Threading.Tasks.ValueTask{T}"/>.
1516
/// </summary>
1617
public interface ISceneLoaderAsync<TAsync> : ISceneLoader
@@ -28,7 +29,7 @@ public interface ISceneLoaderAsync<TAsync> : ISceneLoader
2829
/// <returns>
2930
/// The loading operation.
3031
/// </returns>
31-
TAsync TransitionToSceneAsync(ILoadSceneInfo targetSceneReference, ILoadSceneInfo intermediateSceneReference = default);
32+
TAsync TransitionToSceneAsync(ILoadSceneInfo targetSceneReference, ILoadSceneInfo intermediateSceneReference = default, Scene externalOriginScene = default);
3233

3334
/// <summary>
3435
/// Async version of the <see cref="ISceneLoader.LoadScene(ILoadSceneInfo, bool)"/>.
@@ -37,7 +38,7 @@ public interface ISceneLoaderAsync<TAsync> : ISceneLoader
3738
/// Reference to the scene that's going to be loaded.
3839
/// </param>
3940
/// <param name="setActive">
40-
/// Should the loaded scene be marked as active? Equivalent to calling <see cref="ISceneManager.SetActiveScene(UnityEngine.SceneManagement.Scene)"/>.
41+
/// Should the loaded scene be marked as active? Equivalent to calling <see cref="ISceneManager.SetActiveScene(Scene)"/>.
4142
/// </param>
4243
/// <param name="progress">
4344
/// Optional <see cref="IProgress{T}"/> reference to report the scene loading progress (ranging from 0 to 1).

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

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -22,59 +22,77 @@ public SceneLoaderAsync(ISceneManager manager)
2222
_manager = manager ?? throw new ArgumentNullException("Cannot create a scene loader with a null Scene Manager");
2323
}
2424

25-
public void TransitionToScene(ILoadSceneInfo targetSceneInfo, ILoadSceneInfo intermediateSceneInfo) => TransitionToSceneAsync(targetSceneInfo, intermediateSceneInfo);
25+
public void TransitionToScene(ILoadSceneInfo targetSceneInfo, ILoadSceneInfo intermediateSceneInfo, Scene externalOriginScene) => TransitionToSceneAsync(targetSceneInfo, intermediateSceneInfo, externalOriginScene);
2626

2727
public void UnloadScene(ILoadSceneInfo sceneInfo) => _ = UnloadSceneAsync(sceneInfo);
2828

2929
public void LoadScene(ILoadSceneInfo sceneInfo, bool setActive) => _ = LoadSceneAsync(sceneInfo, setActive);
3030

31-
public ValueTask<Scene> TransitionToSceneAsync(ILoadSceneInfo targetSceneInfo, ILoadSceneInfo intermediateSceneInfo) => intermediateSceneInfo == null ? TransitionDirectlyAsync(targetSceneInfo) : TransitionWithIntermediateAsync(targetSceneInfo, intermediateSceneInfo);
31+
public ValueTask<Scene> TransitionToSceneAsync(ILoadSceneInfo targetSceneInfo, ILoadSceneInfo intermediateSceneInfo, Scene externalOriginScene) => intermediateSceneInfo == null ? TransitionDirectlyAsync(targetSceneInfo, externalOriginScene) : TransitionWithIntermediateAsync(targetSceneInfo, intermediateSceneInfo, externalOriginScene);
3232

3333
public ValueTask<Scene> LoadSceneAsync(ILoadSceneInfo sceneInfo, bool setActive = false, IProgress<float> progress = null) => _manager.LoadSceneAsync(sceneInfo, setActive, progress);
3434

3535
public ValueTask<Scene> UnloadSceneAsync(ILoadSceneInfo sceneInfo) => _manager.UnloadSceneAsync(sceneInfo);
3636

37-
async ValueTask<Scene> TransitionWithIntermediateAsync(ILoadSceneInfo targetSceneInfo, ILoadSceneInfo intermediateSceneInfo)
37+
async ValueTask<Scene> TransitionDirectlyAsync(ILoadSceneInfo loadSceneInfo, Scene externalOriginScene)
3838
{
39-
var currentScene = _manager.GetActiveScene();
39+
var externalOrigin = externalOriginScene.IsValid();
40+
var currentScene = externalOrigin ? externalOriginScene : _manager.GetActiveScene();
41+
await UnloadCurrentScene(currentScene, externalOrigin);
42+
return await LoadSceneAsync(loadSceneInfo, true);
43+
}
44+
45+
async ValueTask<Scene> TransitionWithIntermediateAsync(ILoadSceneInfo targetSceneInfo, ILoadSceneInfo intermediateSceneInfo, Scene externalOriginScene)
46+
{
47+
var externalOrigin = externalOriginScene.IsValid();
48+
var currentScene = externalOrigin ? externalOriginScene : _manager.GetActiveScene();
4049
await _manager.LoadSceneAsync(intermediateSceneInfo);
4150

4251
var loadingBehavior = Object.FindObjectOfType<LoadingBehavior>();
43-
Scene loadedScene;
44-
if (loadingBehavior)
45-
{
46-
var progress = loadingBehavior.Progress;
47-
while (progress.State != LoadingState.Loading)
48-
await Task.Yield();
52+
return loadingBehavior
53+
? await TransitionWithIntermediateLoadingAsync(targetSceneInfo, intermediateSceneInfo, loadingBehavior, currentScene, externalOrigin)
54+
: await TransitionWithIntermediateNoLoadingAsync(targetSceneInfo, intermediateSceneInfo, currentScene, externalOrigin);
55+
}
4956

50-
if (currentScene.IsValid())
51-
await UnloadSceneAsync(new LoadSceneInfoScene(currentScene));
57+
async ValueTask<Scene> TransitionWithIntermediateLoadingAsync(ILoadSceneInfo targetSceneInfo, ILoadSceneInfo intermediateSceneInfo, LoadingBehavior loadingBehavior, Scene currentScene, bool externalOrigin)
58+
{
59+
var progress = loadingBehavior.Progress;
60+
while (progress.State != LoadingState.Loading)
61+
await Task.Yield();
5262

53-
loadedScene = await _manager.LoadSceneAsync(targetSceneInfo, true, progress);
54-
progress.SetState(LoadingState.TargetSceneLoaded);
63+
await UnloadCurrentScene(currentScene, externalOrigin);
5564

56-
while (progress.State != LoadingState.TransitionComplete)
57-
await Task.Yield();
65+
var loadedScene = await _manager.LoadSceneAsync(targetSceneInfo, true, progress);
66+
progress.SetState(LoadingState.TargetSceneLoaded);
5867

59-
_ = UnloadSceneAsync(intermediateSceneInfo);
60-
}
61-
else
62-
{
63-
if (currentScene.IsValid())
64-
await UnloadSceneAsync(new LoadSceneInfoScene(currentScene));
65-
loadedScene = await LoadSceneAsync(targetSceneInfo, true);
66-
_ = UnloadSceneAsync(intermediateSceneInfo);
67-
}
68+
while (progress.State != LoadingState.TransitionComplete)
69+
await Task.Yield();
6870

71+
_ = UnloadSceneAsync(intermediateSceneInfo);
6972
return loadedScene;
7073
}
7174

72-
async ValueTask<Scene> TransitionDirectlyAsync(ILoadSceneInfo loadSceneInfo)
75+
async ValueTask<Scene> TransitionWithIntermediateNoLoadingAsync(ILoadSceneInfo targetSceneInfo, ILoadSceneInfo intermediateSceneInfo, Scene currentScene, bool externalOrigin)
7376
{
74-
var currentScene = _manager.GetActiveScene();
75-
if (currentScene.IsValid())
76-
await UnloadSceneAsync(new LoadSceneInfoScene(currentScene));
77-
return await LoadSceneAsync(loadSceneInfo, true);
77+
await UnloadCurrentScene(currentScene, externalOrigin);
78+
var loadedScene = await LoadSceneAsync(targetSceneInfo, true);
79+
_ = UnloadSceneAsync(intermediateSceneInfo);
80+
return loadedScene;
81+
}
82+
83+
async ValueTask UnloadCurrentScene(Scene currentScene, bool externalOrigin)
84+
{
85+
if (!currentScene.IsValid())
86+
return;
87+
88+
if (externalOrigin)
89+
{
90+
var operation = UnityEngine.SceneManagement.SceneManager.UnloadSceneAsync(currentScene);
91+
while (!operation.isDone)
92+
await Task.Yield();
93+
}
94+
else
95+
await _manager.UnloadSceneAsync(new LoadSceneInfoScene(currentScene));
7896
}
7997
}
8098
}

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

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
* Created on: 9/4/2022 (en-US)
55
*/
66

7+
using Cysharp.Threading.Tasks;
78
using System;
89
using System.Collections;
910
using UnityEngine;
11+
using UnityEngine.SceneManagement;
1012
using Object = UnityEngine.Object;
1113

1214
namespace MyGameDevTools.SceneLoading
@@ -22,13 +24,13 @@ public SceneLoaderCoroutine(ISceneManager manager)
2224
_manager = manager ?? throw new ArgumentNullException("Cannot create a scene loader with a null Scene Manager");
2325
}
2426

25-
public void TransitionToScene(ILoadSceneInfo targetSceneInfo, ILoadSceneInfo intermediateSceneInfo) => TransitionToSceneAsync(targetSceneInfo, intermediateSceneInfo);
27+
public void TransitionToScene(ILoadSceneInfo targetSceneInfo, ILoadSceneInfo intermediateSceneInfo, Scene externalOriginScene) => TransitionToSceneAsync(targetSceneInfo, intermediateSceneInfo, externalOriginScene);
2628

2729
public void UnloadScene(ILoadSceneInfo sceneInfo) => UnloadSceneAsync(sceneInfo);
2830

2931
public void LoadScene(ILoadSceneInfo sceneInfo, bool setActive) => LoadSceneAsync(sceneInfo, setActive);
3032

31-
public Coroutine TransitionToSceneAsync(ILoadSceneInfo targetSceneInfo, ILoadSceneInfo intermediateSceneInfo) => RoutineBehaviour.Instance.StartCoroutine(intermediateSceneInfo == null ? TransitionDirectlyRoutine(targetSceneInfo) : TransitionWithIntermediateRoutine(targetSceneInfo, intermediateSceneInfo));
33+
public Coroutine TransitionToSceneAsync(ILoadSceneInfo targetSceneInfo, ILoadSceneInfo intermediateSceneInfo, Scene externalOriginScene) => RoutineBehaviour.Instance.StartCoroutine(intermediateSceneInfo == null ? TransitionDirectlyRoutine(targetSceneInfo, externalOriginScene) : TransitionWithIntermediateRoutine(targetSceneInfo, intermediateSceneInfo, externalOriginScene));
3234

3335
public Coroutine UnloadSceneAsync(ILoadSceneInfo sceneInfo) => RoutineBehaviour.Instance.StartCoroutine(UnloadRoutine(sceneInfo));
3436

@@ -44,42 +46,57 @@ IEnumerator UnloadRoutine(ILoadSceneInfo sceneInfo)
4446
yield return new WaitTask(_manager.UnloadSceneAsync(sceneInfo).AsTask());
4547
}
4648

47-
IEnumerator TransitionWithIntermediateRoutine(ILoadSceneInfo targetSceneInfo, ILoadSceneInfo intermediateSceneInfo)
49+
IEnumerator TransitionDirectlyRoutine(ILoadSceneInfo targetSceneInfo, Scene externalOriginScene)
4850
{
49-
var currentScene = _manager.GetActiveScene();
51+
var externalOrigin = externalOriginScene.IsValid();
52+
var currentScene = externalOrigin ? externalOriginScene : _manager.GetActiveScene();
53+
yield return UnloadCurrentScene(currentScene, externalOrigin);
54+
yield return LoadRoutine(targetSceneInfo, true, null);
55+
}
56+
57+
IEnumerator TransitionWithIntermediateRoutine(ILoadSceneInfo targetSceneInfo, ILoadSceneInfo intermediateSceneInfo, Scene externalOriginScene)
58+
{
59+
var externalOrigin = externalOriginScene.IsValid();
60+
var currentScene = externalOrigin ? externalOriginScene : _manager.GetActiveScene();
5061
yield return new WaitTask(_manager.LoadSceneAsync(intermediateSceneInfo).AsTask());
5162

5263
var loadingBehavior = Object.FindObjectOfType<LoadingBehavior>();
53-
if (loadingBehavior)
54-
{
55-
var progress = loadingBehavior.Progress;
56-
yield return new WaitUntil(() => progress.State == LoadingState.Loading);
64+
yield return loadingBehavior
65+
? TransitionWithIntermediateLoadingAsync(targetSceneInfo, intermediateSceneInfo, loadingBehavior, currentScene, externalOrigin)
66+
: TransitionWithIntermediateNoLoadingAsync(targetSceneInfo, intermediateSceneInfo, currentScene, externalOrigin);
67+
}
68+
69+
IEnumerator TransitionWithIntermediateLoadingAsync(ILoadSceneInfo targetSceneInfo, ILoadSceneInfo intermediateSceneInfo, LoadingBehavior loadingBehavior, Scene currentScene, bool externalOrigin)
70+
{
71+
var progress = loadingBehavior.Progress;
72+
yield return new WaitUntil(() => progress.State == LoadingState.Loading);
5773

58-
if (currentScene.IsValid())
59-
yield return UnloadRoutine(new LoadSceneInfoScene(currentScene));
74+
yield return UnloadCurrentScene(currentScene, externalOrigin);
6075

61-
yield return new WaitTask(_manager.LoadSceneAsync(targetSceneInfo, true, progress).AsTask());
62-
progress.SetState(LoadingState.TargetSceneLoaded);
76+
yield return new WaitTask(_manager.LoadSceneAsync(targetSceneInfo, true, progress).AsTask());
77+
progress.SetState(LoadingState.TargetSceneLoaded);
6378

64-
yield return new WaitUntil(() => progress.State == LoadingState.TransitionComplete);
79+
yield return new WaitUntil(() => progress.State == LoadingState.TransitionComplete);
6580

66-
UnloadSceneAsync(intermediateSceneInfo);
67-
}
68-
else
69-
{
70-
if (currentScene.IsValid())
71-
yield return UnloadRoutine(new LoadSceneInfoScene(currentScene));
72-
yield return LoadRoutine(targetSceneInfo, true, null);
73-
UnloadSceneAsync(intermediateSceneInfo);
74-
}
81+
UnloadSceneAsync(intermediateSceneInfo);
7582
}
7683

77-
IEnumerator TransitionDirectlyRoutine(ILoadSceneInfo targetSceneInfo)
84+
IEnumerator TransitionWithIntermediateNoLoadingAsync(ILoadSceneInfo targetSceneInfo, ILoadSceneInfo intermediateSceneInfo, Scene currentScene, bool externalOrigin)
7885
{
79-
var currentScene = _manager.GetActiveScene();
80-
if (currentScene.IsValid())
81-
yield return UnloadRoutine(new LoadSceneInfoScene(currentScene));
86+
yield return UnloadCurrentScene(currentScene, externalOrigin);
8287
yield return LoadRoutine(targetSceneInfo, true, null);
88+
UnloadSceneAsync(intermediateSceneInfo);
89+
}
90+
91+
IEnumerator UnloadCurrentScene(Scene currentScene, bool externalOrigin)
92+
{
93+
if (!currentScene.IsValid())
94+
yield break;
95+
96+
if (externalOrigin)
97+
yield return UnityEngine.SceneManagement.SceneManager.UnloadSceneAsync(currentScene);
98+
else
99+
yield return UnloadRoutine(new LoadSceneInfoScene(currentScene));
83100
}
84101
}
85102
}

0 commit comments

Comments
 (0)