88using System . Collections . Generic ;
99using System . Runtime . CompilerServices ;
1010using System . Text ;
11+ using System . Threading ;
1112using System . Threading . Tasks ;
1213using UnityEngine ;
1314using UnityEngine . SceneManagement ;
@@ -28,9 +29,19 @@ public class SceneManager : ISceneManager, ISceneManagerReporter
2829
2930 readonly List < Scene > _unloadingScenes = new List < Scene > ( ) ;
3031 readonly List < Scene > _loadedScenes = new List < Scene > ( ) ;
32+ readonly CancellationTokenSource _lifetimeToken = new CancellationTokenSource ( ) ;
3133
3234 Scene _activeScene ;
3335
36+ public void Dispose ( )
37+ {
38+ _lifetimeToken . Cancel ( ) ;
39+ _lifetimeToken . Dispose ( ) ;
40+
41+ _unloadingScenes . Clear ( ) ;
42+ _loadedScenes . Clear ( ) ;
43+ }
44+
3445 public void SetActiveScene ( Scene scene )
3546 {
3647 var validScene = scene . IsValid ( ) ;
@@ -69,7 +80,89 @@ public Scene GetLoadedSceneByName(string name)
6980 throw new ArgumentException ( $ "[{ GetType ( ) . Name } ] Could not find any loaded scene with the name '{ name } '.", nameof ( name ) ) ;
7081 }
7182
72- public async ValueTask < Scene [ ] > LoadScenesAsync ( ILoadSceneInfo [ ] sceneInfos , int setIndexActive = - 1 , IProgress < float > progress = null )
83+ public async ValueTask < Scene [ ] > LoadScenesAsync ( ILoadSceneInfo [ ] sceneInfos , int setIndexActive = - 1 , IProgress < float > progress = null , CancellationToken token = default )
84+ {
85+ CancellationTokenSource linkedSource = CancellationTokenSource . CreateLinkedTokenSource ( _lifetimeToken . Token , token ) ;
86+ try
87+ {
88+ return await LoadScenesAsync_Internal ( sceneInfos , setIndexActive , progress , linkedSource . Token ) ;
89+ }
90+ catch ( OperationCanceledException cancelException )
91+ {
92+ Debug . LogWarningFormat ( "[{0}] LoadScenesAsync was canceled. Exception:\n {1}" , GetType ( ) . Name , cancelException ) ;
93+ throw ;
94+ }
95+ finally
96+ {
97+ linkedSource . Dispose ( ) ;
98+ }
99+ }
100+
101+ public async ValueTask < Scene > LoadSceneAsync ( ILoadSceneInfo sceneInfo , bool setActive = false , IProgress < float > progress = null , CancellationToken token = default )
102+ {
103+ sceneInfo = sceneInfo ?? throw new NullReferenceException ( $ "[{ GetType ( ) . Name } ] Provided scene info is null.") ;
104+
105+ CancellationTokenSource linkedSource = CancellationTokenSource . CreateLinkedTokenSource ( _lifetimeToken . Token , token ) ;
106+ Scene [ ] loadedScenes = null ;
107+ try
108+ {
109+ loadedScenes = await LoadScenesAsync_Internal ( new ILoadSceneInfo [ ] { sceneInfo } , setActive ? 0 : - 1 , progress , linkedSource . Token ) ;
110+ }
111+ catch ( OperationCanceledException cancelException )
112+ {
113+ Debug . LogWarningFormat ( "[{0}] LoadSceneAsync was canceled. Exception:\n {1}" , GetType ( ) . Name , cancelException ) ;
114+ throw ;
115+ }
116+ finally
117+ {
118+ linkedSource . Dispose ( ) ;
119+ }
120+
121+ return loadedScenes != null && loadedScenes . Length > 0 ? loadedScenes [ 0 ] : default ;
122+ }
123+
124+ public async ValueTask < Scene [ ] > UnloadScenesAsync ( ILoadSceneInfo [ ] sceneInfos , CancellationToken token = default )
125+ {
126+ CancellationTokenSource linkedSource = CancellationTokenSource . CreateLinkedTokenSource ( _lifetimeToken . Token , token ) ;
127+ try
128+ {
129+ return await UnloadScenesAsync_Internal ( sceneInfos , linkedSource . Token ) ;
130+ }
131+ catch ( OperationCanceledException cancelException )
132+ {
133+ Debug . LogWarningFormat ( "[{0}] UnloadScenesAsync was canceled. Exception:\n {1}" , GetType ( ) . Name , cancelException ) ;
134+ throw ;
135+ }
136+ finally
137+ {
138+ linkedSource . Dispose ( ) ;
139+ }
140+ }
141+
142+ public async ValueTask < Scene > UnloadSceneAsync ( ILoadSceneInfo sceneInfo , CancellationToken token = default )
143+ {
144+ sceneInfo = sceneInfo ?? throw new ArgumentNullException ( nameof ( sceneInfo ) , $ "[{ GetType ( ) . Name } ] Provided scene info is null.") ;
145+
146+ CancellationTokenSource linkedSource = CancellationTokenSource . CreateLinkedTokenSource ( _lifetimeToken . Token , token ) ;
147+ Scene [ ] unloadedScenes = null ;
148+ try
149+ {
150+ unloadedScenes = await UnloadScenesAsync_Internal ( new ILoadSceneInfo [ ] { sceneInfo } , linkedSource . Token ) ;
151+ }
152+ catch ( OperationCanceledException cancelException )
153+ {
154+ Debug . LogWarningFormat ( "[{0}] UnloadSceneAsync was canceled. Exception:\n {1}" , GetType ( ) . Name , cancelException ) ;
155+ throw ;
156+ }
157+ finally
158+ {
159+ linkedSource . Dispose ( ) ;
160+ }
161+
162+ return unloadedScenes != null && unloadedScenes . Length > 0 ? unloadedScenes [ 0 ] : default ;
163+ }
164+
165+ async ValueTask < Scene [ ] > LoadScenesAsync_Internal ( ILoadSceneInfo [ ] sceneInfos , int setIndexActive , IProgress < float > progress , CancellationToken token )
73166 {
74167 if ( sceneInfos == null || sceneInfos . Length == 0 )
75168 throw new ArgumentException ( nameof ( sceneInfos ) , $ "[{ GetType ( ) . Name } ] Provided scene group is null or empty.") ;
@@ -78,18 +171,20 @@ public async ValueTask<Scene[]> LoadScenesAsync(ILoadSceneInfo[] sceneInfos, int
78171
79172 var operationGroup = GetLoadSceneOperations ( sceneInfos , ref setIndexActive ) ;
80173 if ( operationGroup . Operations . Count == 0 )
81- return Array . Empty < Scene > ( ) ;
174+ throw new InvalidOperationException ( $ "[ { GetType ( ) . Name } Provided scene group was not able to generate any valid load scene operations." ) ;
82175
83- while ( ! operationGroup . IsDone )
176+ while ( ! operationGroup . IsDone && ! token . IsCancellationRequested )
84177 {
85178#if USE_UNITASK
86- await UniTask . Yield ( ) ;
179+ await UniTask . Yield ( token ) ;
87180#else
88181 await Task . Yield ( ) ;
89182#endif
90183 progress ? . Report ( operationGroup . Progress ) ;
91184 }
92185
186+ token . ThrowIfCancellationRequested ( ) ;
187+
93188 var loadedScenes = GetLastUnityLoadedScenesByInfos ( sceneInfos , ref setIndexActive ) ;
94189
95190 _loadedScenes . AddRange ( loadedScenes ) ;
@@ -102,23 +197,14 @@ public async ValueTask<Scene[]> LoadScenesAsync(ILoadSceneInfo[] sceneInfos, int
102197 return loadedScenes ;
103198 }
104199
105- public async ValueTask < Scene > LoadSceneAsync ( ILoadSceneInfo sceneInfo , bool setActive = false , IProgress < float > progress = null )
106- {
107- sceneInfo = sceneInfo ?? throw new NullReferenceException ( $ "[{ GetType ( ) . Name } ] Provided scene info is null.") ;
108- var loadedScenes = await LoadScenesAsync ( new ILoadSceneInfo [ ] { sceneInfo } , setActive ? 0 : - 1 , progress ) ;
109- if ( loadedScenes . Length == 0 )
110- return default ;
111- return loadedScenes [ 0 ] ;
112- }
113-
114- public async ValueTask < Scene [ ] > UnloadScenesAsync ( ILoadSceneInfo [ ] sceneInfos )
200+ async ValueTask < Scene [ ] > UnloadScenesAsync_Internal ( ILoadSceneInfo [ ] sceneInfos , CancellationToken token )
115201 {
116202 if ( sceneInfos == null || sceneInfos . Length == 0 )
117203 throw new ArgumentException ( $ "[{ GetType ( ) . Name } ] Provided scene group is null or empty.", nameof ( sceneInfos ) ) ;
118204
119205 var loadedScenes = GetLastLoadedScenesByInfos ( sceneInfos , out var unloadingIndexes ) ;
120206 if ( loadedScenes . Count == 0 )
121- return Array . Empty < Scene > ( ) ;
207+ throw new InvalidOperationException ( $ "[ { GetType ( ) . Name } Provided scene group was not able to generate any valid unload scene operations." ) ;
122208
123209 int unloadingLength = unloadingIndexes . Length ;
124210 var unloadingScenes = new Scene [ unloadingLength ] ;
@@ -137,12 +223,16 @@ public async ValueTask<Scene[]> UnloadScenesAsync(ILoadSceneInfo[] sceneInfos)
137223 _unloadingScenes . Add ( scene ) ;
138224 }
139225
140- while ( ! operationGroup . IsDone )
226+ while ( ! operationGroup . IsDone && ! token . IsCancellationRequested )
227+ {
141228#if USE_UNITASK
142- await UniTask . Yield ( ) ;
229+ await UniTask . Yield ( token ) ;
143230#else
144231 await Task . Yield ( ) ;
145232#endif
233+ }
234+
235+ token . ThrowIfCancellationRequested ( ) ;
146236
147237 foreach ( var scene in loadedScenes )
148238 {
@@ -154,7 +244,7 @@ public async ValueTask<Scene[]> UnloadScenesAsync(ILoadSceneInfo[] sceneInfos)
154244
155245 var tasks = new Task [ unloadingLength ] ;
156246 for ( i = 0 ; i < unloadingLength ; i ++ )
157- tasks [ i ] = WaitForSceneUnload ( unloadingScenes [ i ] ) . AsTask ( ) ;
247+ tasks [ i ] = WaitForSceneUnload ( unloadingScenes [ i ] , token ) . AsTask ( ) ;
158248
159249 await Task . WhenAll ( tasks ) ;
160250
@@ -163,23 +253,16 @@ public async ValueTask<Scene[]> UnloadScenesAsync(ILoadSceneInfo[] sceneInfos)
163253 return loadedScenes . ToArray ( ) ;
164254 }
165255
166- public async ValueTask < Scene > UnloadSceneAsync ( ILoadSceneInfo sceneInfo )
167- {
168- sceneInfo = sceneInfo ?? throw new ArgumentNullException ( nameof ( sceneInfo ) , $ "[{ GetType ( ) . Name } ] Provided scene info is null.") ;
169- var unloadedScenes = await UnloadScenesAsync ( new ILoadSceneInfo [ ] { sceneInfo } ) ;
170- if ( unloadedScenes . Length == 0 )
171- return default ;
172- return unloadedScenes [ 0 ] ;
173- }
174-
175- async ValueTask < Scene > WaitForSceneUnload ( Scene scene )
256+ async ValueTask < Scene > WaitForSceneUnload ( Scene scene , CancellationToken token )
176257 {
177258#if USE_UNITASK
178- await UniTask . WaitUntil ( ( ) => ! _unloadingScenes . Contains ( scene ) ) ;
259+ await UniTask . WaitUntil ( ( ) => ! _unloadingScenes . Contains ( scene ) , cancellationToken : token ) ;
179260#else
180- while ( _unloadingScenes . Contains ( scene ) )
261+ while ( _unloadingScenes . Contains ( scene ) && ! token . IsCancellationRequested )
181262 await Task . Yield ( ) ;
182263#endif
264+ token . ThrowIfCancellationRequested ( ) ;
265+
183266 return scene ;
184267 }
185268
0 commit comments