Skip to content

Commit 320f944

Browse files
committed
Update documentation
1 parent 1b999f8 commit 320f944

File tree

1 file changed

+265
-17
lines changed

1 file changed

+265
-17
lines changed

README.md

Lines changed: 265 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
My Unity Tools - Scene Loading
66
===
77

8-
_Collection of tools to improve scene management and transitions in Unity._
8+
_A package that standardizes the scene loading process among many different possibilities, including support for Coroutines, C# Tasks, UniTask and Addressables._
99

1010
Installation
1111
---
@@ -28,29 +28,277 @@ You should add this to your `manifest.json` under the `Packages` folder on the r
2828
Overview
2929
---
3030

31-
Loading scenes in Unity is very simple, but developing games sometimes require more flexible implementations. This package has two main focuses:
32-
33-
1. Cover the most common uses of Scene Loading
34-
2. Provide `awaitable` implementations
35-
31+
Loading scenes in Unity is very simple, but developing games sometimes require more flexible implementations. This package aims to simplify common use cases for scene loading.
3632
Additionally, it offers support for [Unity Addressables](https://docs.unity3d.com/Manual/com.unity.addressables.html) and for [UniTask](https://github.com/Cysharp/UniTask) with no additional setup required.
3733

38-
The paradigm for Scene Loading relies in the following actions:
39-
* **Load**: loads a new scene on top of the current scene structure.
40-
* **Unload**: unloads one of the scenes in the scene structure.
41-
* **Switch**: replaces the current active scene with a new one (executes both unload and load operations).
42-
* **Transition**: switches to another scene but with an intermediate "loading scene" in between.
34+
Aside from the ordinary **Load** and **Unload** actions, the Scene Loading tools introduce the **Transition** as a new standard to control transitions between scenes with an optional intermediate "loading scene" in between.
4335

44-
Keep that in mind when using the scene loader utilities.
36+
:information_source: You don't need to understand what **Addressables** or **UniTask** do in order to use this package. There are scene loaders that only rely on basic Unity Engine functionalities.
4537

4638
Usage
4739
---
4840

49-
There are **four** scene loaders that you can use, depending on your project necessities:
50-
1. `AsyncSceneLoader`: simple scene loader with `awaitable` instructions.
51-
2. `UniTaskSceneLoader`: the `AsyncSceneLoader` with `UniTask` instead of `Task`.
52-
3. `AddressableAsyncSceneLoader`: a scene loader that handles Addressable scenes with `awaitable` instructions.
53-
4. `AddressableUniTaskSceneLoader`: the `AddressableSceneLoader` with `UniTask` instead of `Task`.
41+
Loading scenes with this package implies that the scenes **will always be loaded as Additive**. That is simply because there is no advantage in loading scenes in the **Single** load scene mode when you expect to work with multiple scenes. There are **six** scene loaders that you can use, depending on your project necessities:
42+
1. `SceneLoaderAsync`: simple scene loader with `awaitable` instructions.
43+
2. `SceneLoaderCoroutine`: simple scene loader with `Coroutine` instructions.
44+
3. `SceneLoaderUniTask`: the `SceneLoaderAsync` with `UniTask` instead of `Task`.
45+
4. `AddressableSceneLoaderAsync`: a scene loader that handles Addressable scenes with `awaitable` instructions.
46+
5. `AddressableSceneLoaderCoroutine`: a scene loader that handles Addressable scenes with `Coroutine` instructions.
47+
6. `AddressableSceneLoaderUniTask`: the `AddressableSceneLoaderAsync` with `UniTask` instead of `Task`.
48+
49+
Before we go into each one, let's understand how we got there. There are some core differences between loading scenes with the `SceneManager` and via Addressables.
50+
51+
- | SceneManager | Addressables
52+
--- | --- | ---
53+
Scene Reference | Build Index, Scene Name or Path | Asset Reference, Address Runtime Key
54+
Active Scene | Managed through `SceneManager` | None
55+
Loaded Scenes | Managed through `SceneManager` | No high level API available
56+
57+
Due to those differences, the Addressable scene loaders had to be split into their own logic. However, in order to simplify the usability, even though the Addressable does not work entirely with the `SceneManager`, the way you interact with its scene loaders is very similar to how you would interact with the others.
58+
59+
### The Scene Loader
60+
61+
The most basic usability you'll get from a scene loader is to **Load** a scene, **Unload** it, or **Transition** to another scene. That's what the `ISceneLoader` interface will define:
62+
63+
```cs
64+
public interface ISceneLoader
65+
{
66+
void TransitionToScene(ILoadSceneInfo targetSceneInfo, ILoadSceneInfo intermediateSceneInfo = null);
67+
68+
void UnloadScene(ILoadSceneInfo sceneInfo);
69+
70+
void LoadScene(ILoadSceneInfo sceneInfo, bool setActive = false);
71+
}
72+
```
73+
74+
Observe that instead of defining `int` or `string` parameters for the scene's build index or name, it defines the `ILoadSceneInfo` interface instead. That is just a way of standardizing the scene information that we'll be working with, instead of creating multiple methods, each with their own implementation.
75+
76+
The `ILoadSceneInfo` defines:
77+
78+
```cs
79+
public interface ILoadSceneInfo
80+
{
81+
AsyncOperation UnloadSceneAsync();
82+
83+
AsyncOperation LoadSceneAsync();
84+
85+
Scene GetScene();
86+
}
87+
```
88+
89+
Then you'll be able to use whatever fits your use case: the `LoadSceneInfoIndex` or the `LoadSceneInfoName` which you can create just as you would expect:
90+
91+
```cs
92+
// Create an ILoadSceneInfo by the scene's build index:
93+
ILoadSceneInfo sceneInfo = new LoadSceneInfoIndex(1);
94+
95+
// Create an ILoadSceneInfo by the scene's name:
96+
ILoadSceneInfo sceneInfo = new LoadSceneInfoName("MainMenu");
97+
```
98+
99+
As a final example, let's suppose you're currently at the **"Main Menu"** scene and you want to transition to the **"Level 1"** scene with the **"Loading Tips"** loading scene. You could for example:
100+
101+
```cs
102+
sceneLoader.TransitionToScene(new LoadSceneInfoName("Level 1"), new LoadSceneInfoName("Loading Tips"));
103+
```
104+
105+
This would trigger the scene transition by loading the **"Loading Tips"** scene first, and then starting to load the **"Level 1"** scene while showing its load progress in the loading scene. Then, when the **"Level 1"** scene is done loading, the **"Loading Tips"** scene will get unloaded and the transition will be complete.
106+
107+
Now, what if you wanted to _await_ this call?
108+
109+
### Awaitable Scene Loaders
110+
111+
You have three options of _awaitable_ scene loaders to choose: **Coroutine**, **C# Task (Async)** and **UniTask**. Coroutine is not C# awaitable, but it works similarly if you `yield return` it inside another Coroutine. If you want to use **UniTask**, make sure you [install its package](https://github.com/Cysharp/UniTask#upm-package) in your project.
112+
113+
Since each implementation changes what you'll get as the return types, they all have their own interfaces:
114+
115+
```cs
116+
public interface ISceneLoaderAsync : ISceneLoader
117+
{
118+
Task TransitionToSceneAsync(ILoadSceneInfo targetSceneInfo, ILoadSceneInfo intermediateSceneInfo = null);
119+
120+
Task UnloadSceneAsync(ILoadSceneInfo sceneInfo);
121+
122+
Task LoadSceneAsync(ILoadSceneInfo sceneInfo, bool setActive = false);
123+
}
124+
125+
public interface ISceneLoaderCoroutine : ISceneLoader
126+
{
127+
Coroutine TransitionToSceneRoutine(ILoadSceneInfo targetSceneInfo, ILoadSceneInfo intermediateSceneInfo = null);
128+
129+
Coroutine UnloadSceneRoutine(ILoadSceneInfo sceneInfo);
130+
131+
Coroutine LoadSceneRoutine(ILoadSceneInfo sceneInfo, bool setActive = false);
132+
}
133+
134+
public interface ISceneLoaderUniTask : ISceneLoader
135+
{
136+
UniTask TransitionToSceneAsync(ILoadSceneInfo targetSceneInfo, ILoadSceneInfo intermediateSceneInfo = null);
137+
138+
UniTask UnloadSceneAsync(ILoadSceneInfo sceneInfo);
139+
140+
UniTask LoadSceneAsync(ILoadSceneInfo sceneInfo, bool setActive = false);
141+
}
142+
```
143+
144+
In the end however, they all also implement `ISceneLoader`, so even though you'll only use the basic `ISceneLoader` methods, you can still take advantage of the `UniTask` implementation, for example. Otherwise, if you're going to await the operation, then you can choose the system that fits you best.
145+
146+
### Addressable Scene Loading
147+
148+
The Addressable Scene Loading introduces a few more concepts in order to keep the simplicity of the regular scene loading process. Take for example the `IAddressableSceneLoader` interface:
149+
150+
```cs
151+
public interface IAddressableSceneLoader
152+
{
153+
IAddressableSceneManager SceneManager { get; }
154+
155+
void TransitionToScene(IAddressableLoadSceneReference targetSceneReference, IAddressableLoadSceneReference intermediateSceneReference = null);
156+
157+
void UnloadScene(IAddressableLoadSceneInfo sceneInfo);
158+
159+
void LoadScene(IAddressableLoadSceneReference sceneReference, bool setActive = false);
160+
}
161+
```
162+
163+
You can see that it has two main differences from the `ISceneLoader`: the presence of the `IAddressableSceneManager` and the different parameter types. Other than that, the usability is exactly the same.
164+
165+
Instead of a single `ILoadSceneInfo` parameter type, the Addressable implementation has two different interfaces: the `IAddressableLoadSceneReference` and the `IAddressableLoadSceneInfo`. This is due to nature of the **Load** and **Unload** scene operations of the Addressables System, in which you need different parameters for each operation.
166+
167+
Take a look for example in the definition of the `IAddressableLoadSceneReference`:
168+
169+
```cs
170+
public interface IAddressableLoadSceneReference
171+
{
172+
AsyncOperationHandle<SceneInstance> LoadSceneAsync(IAddressableSceneManager sceneManager);
173+
}
174+
```
175+
176+
It only defines the **Load** operation that returns an `AsyncOperationHandle<SceneInstance>`. To create an object that defines this interface, you can either use the scene's `AssetReference` or its runtime key, which is how you normally reference assets with Addressables.
177+
178+
```cs
179+
// Create an IAddressableLoadSceneReference by the scene's AssetReference:
180+
IAddressableLoadSceneReference sceneReference = new AddressableLoadSceneReferenceAsset(sceneAssetReference);
181+
182+
// Create an IAddressableLoadSceneReference by the scene's runtime key:
183+
IAddressableLoadSceneReference sceneReference = new AddressableLoadSceneReferenceKey("MainMenu");
184+
```
185+
186+
Now, when unloading scenes, you'll use the `IAddressableLoadSceneInfo` interface:
187+
188+
```cs
189+
public interface IAddressableLoadSceneInfo
190+
{
191+
AsyncOperationHandle<SceneInstance> UnloadSceneAsync(IAddressableSceneManager sceneManager, bool autoReleaseHandle = true);
192+
}
193+
```
194+
195+
Although very similar to the **Load** operation, it's important to note here that you'll need different scene information in order to create an object that implements this method. You'll need either the `AsyncOperationHandle<SceneInstance>` that was used to load the scene, or the `SceneInstance` itself, or in last case the loaded scene name.
196+
197+
```cs
198+
// Create an IAddressableLoadSceneInfo by the scene's AsyncOperationHandle<SceneInstance>:
199+
IAddressableLoadSceneInfo sceneInfo = new AddressableLoadSceneInfoOperationHandle(sceneOperationHandle);
200+
201+
// Create an IAddressableLoadSceneInfo by the scene's SceneInstance:
202+
IAddressableLoadSceneInfo sceneInfo = new AddressableLoadSceneInfoInstance(sceneInstance);
203+
204+
// Create an IAddressableLoadSceneInfo by the scene's name:
205+
IAddressableLoadSceneInfo sceneInfo = new AddressableLoadSceneInfoName("Main Menu");
206+
```
207+
208+
As you could see, both `IAddressableLoadSceneReference` and `IAddressableLoadSceneInfo` require the `IAddressableSceneManager` as a parameter of their methods.
209+
210+
### The Addressable Scene Manager
211+
212+
Loading scenes through Addressables does not exactly pass through the `SceneManager`. It does fire callbacks for loading and unloading scenes, but there's no way to get the active scene or to get any of the loaded scenes, if they have been loaded through the Addressables System. For that reason, the `IAddressableSceneManager` defines a standard for an Addressable Scene Manager that keeps track of the loaded scenes and of an active scene as well, just like you'd expect with the regular `SceneManager`.
213+
214+
It's only going to be used internally by the scene loaders, but take a look at its definition:
215+
216+
```cs
217+
public interface IAddressableSceneManager
218+
{
219+
void SetActiveSceneHandle(AsyncOperationHandle<SceneInstance> sceneHandle);
220+
221+
AsyncOperationHandle<SceneInstance> GetActiveSceneHandle();
222+
223+
AsyncOperationHandle<SceneInstance> LoadSceneAsync(AssetReference sceneReference);
224+
AsyncOperationHandle<SceneInstance> LoadSceneAsync(string runtimeKey);
225+
226+
AsyncOperationHandle<SceneInstance> UnloadSceneAsync(AsyncOperationHandle<SceneInstance> sceneHandle, bool autoReleaseHandle = true);
227+
AsyncOperationHandle<SceneInstance> UnloadSceneAsync(SceneInstance scene, bool autoReleaseHandle = true);
228+
AsyncOperationHandle<SceneInstance> UnloadSceneAsync(string sceneName, bool autoReleaseHandle = true);
229+
230+
AsyncOperationHandle<SceneInstance> GetLoadedSceneHandle(SceneInstance sceneInstance);
231+
AsyncOperationHandle<SceneInstance> GetLoadedSceneHandle(string sceneName);
232+
}
233+
```
234+
235+
Not only it provides methods for loading and unloading scenes with the many ways to reference them, but it also keeps track of the loaded scenes and manages the current active scene. The `IAddressableLoadSceneReference` and `IAddressableLoadSceneInfo` implementations require the scene manager for calling these methods, just like in the non-addressable implementations, they also do it but statically via the `SceneManager`.
236+
237+
Now we can load addressable scenes, but what about _awaiting_ them too?
238+
239+
### Awaitable Addressables
240+
241+
The Addressables System has much better support for awaiting operations than the other Unity Engine systems. While it can be easier to implement, we still need to adapt it to work with the defined standards in the previous topics. So, not so different from the non-addressable implementation, we have the three options for awaitable scene loaders:
242+
243+
```cs
244+
public interface IAddressableSceneLoaderAsync : IAddressableSceneLoader
245+
{
246+
Task<SceneInstance> TransitionToSceneAsync(IAddressableLoadSceneReference targetSceneReference, IAddressableLoadSceneReference intermediateSceneReference = null);
247+
248+
Task<SceneInstance> LoadSceneAsync(IAddressableLoadSceneReference sceneReference, bool setActive = false);
249+
250+
Task UnloadSceneAsync(IAddressableLoadSceneInfo sceneInfo);
251+
}
252+
253+
public interface IAddressableSceneLoaderCoroutine : IAddressableSceneLoader
254+
{
255+
Coroutine TransitionToSceneRoutine(IAddressableLoadSceneReference targetSceneReference, IAddressableLoadSceneReference intermediateSceneReference = null);
256+
257+
Coroutine LoadSceneRoutine(IAddressableLoadSceneReference sceneReference, bool setActive = false);
258+
259+
Coroutine UnloadSceneRoutine(IAddressableLoadSceneInfo sceneInfo);
260+
}
261+
262+
public interface IAddressableSceneLoaderUniTask : IAddressableSceneLoader
263+
{
264+
UniTask<SceneInstance> TransitionToSceneAsync(IAddressableLoadSceneReference targetSceneReference, IAddressableLoadSceneReference intermediateSceneReference = null);
265+
266+
UniTask<SceneInstance> LoadSceneAsync(IAddressableLoadSceneReference sceneReference, bool setActive = false);
267+
268+
UniTask UnloadSceneAsync(IAddressableLoadSceneInfo sceneInfo);
269+
}
270+
```
271+
272+
Just like before, they also implement the `IAddressableSceneLoader` so you can use what you prefer.
273+
274+
### Why so many interfaces?
275+
276+
The idea behind the interfaces is first to decouple things and second to allow you to build your own systems if you require something very different from the provided content. Sometimes projects require very specific implementations, and instead of making the system extremely complex and detailed, I'd rather have it broken into many different pieces that you can replace to fit with whatever works best in each use case.
277+
278+
I am always open to suggestions, so please if you have any, don't hesistate to share!
279+
280+
Samples
281+
---
282+
283+
This package offers samples with each of the scene loaders for you to use as a starting point. To use them, simply import the desired sample through the Package Manager.
284+
285+
### For non-Addressable scene loaders:
286+
287+
Make sure you add all scenes to the build settings with the following indexes:
288+
289+
0. SceneA
290+
1. SceneB
291+
2. SceneLoading
292+
3. SceneC_add
293+
4. SceneD_add
294+
295+
You can try out the sample by loading either the SceneA or SceneB and hitting play in the Unity Editor.
296+
297+
### For Addressable scene loaders:
298+
299+
Make sure you mark all scenes as addressables and simplify their names in the Addressable Groups window. To test, open up the **Bootstrap** scene and hit play.
300+
301+
Check if your Addressables Play Mode Script is `Use Asset Database`, otherwise you may be required to build the Addressable groups.
54302

55303
---
56304

0 commit comments

Comments
 (0)