In many of my games I have complex multi-scene setups that greatly utilize additive scene loading.
Issue is — I can’t guarantee that each scene will have a unique name.
Frequently I have run into these situations where I might be loading in two instances of the same scene at the same time, which results in there being no safe or reliable way to find which scene was loaded from a particular AsyncOperation (which is an absolute necessity for me).
I have two possible solutions that I am proposing to solve this problem:
Solution 1:
The first solution is simple little ‘result’ property on an AsyncOperation, like this:
AsyncOperation operation = SceneManager.LoadScene("Scene_1");
// operation.Result is a Nullable<Scene>.
Debug.Log(operation.Result.HasValue); // False — the scene has not loaded yet!
while (!operation.isCompleted) yield return null;
Debug.Log(operation.Result.HasValue); // True — the scene has loaded!
The little ‘Result’ property allows easy access to the loaded scene (if the scene has finished loading).
Solution 2:
The second solution is a little less standard, but overall more clean for AsyncOperations that were Unloading Operations, since no ‘Result’ property is necessary for them.
AsyncOperation operation = SceneManager.LoadScene("Scene_1");
// Returns the scene being loaded or has finished loading — if it still exists.
var scene = SceneManager.GetSceneAssociatedWithOperation(operation);
The code is a bit more magical, but a fair bit nicer syntactically speaking. All that is required to gain access to the scene that was loaded is the AsyncOperation handle that was used to load the scene.
Internally, Unity will hold a nice little dictionary of AsyncOperation keys and Scene values to support the feature. So really, the method is just a disguised dictionary lookup.
Would it be possible to add something like either of these two solutions in the future?
Preferably on the sooner side?
Just throw a SceneIdentifier script into the scene somewhere.
That will serve two purposes:
finding it will inform the rest of the game that the scene has fully loaded, no need to watch random async stuff
you can study it to decide what it is
Bonus: you can pack tons of other info into this identifier and use it.
In fact, here’s one of mine stripped down for you:
using UnityEngine;
// @kurtdekker
public class SceneIdentifier : MonoBehaviour
{
public string id;
#if UNITY_EDITOR
void Reset()
{
// default it to the name of the scene
id = gameObject.scene.name;
// name the GameObject for obviousity
name = GetType().ToString();
}
#endif
}
Hmm, I think I see what you are getting at.
But I don’t think this solves my particular scenario.
Let me explain —
My Particular Scenario
In my game, I have multiple chunks of the world separated into individual scenes which I refer to as “Stages”. Each of these Stages are dynamically loaded at runtime — and there can be any number of the same Stage active at the same time.
Each time a Stage is asked to be loaded (via providing a valid ‘Stage Scene’), the Scene that the Stage corresponds to is loaded in. When this happens, multiple transitions play that target the newly loaded Scene’s Stage to mask this entire process with spiffy VFX.
The problem I am having is that multiple of the same Stage (A.K.A Scene) can be loaded at once and even loaded in parallel. In these instances, I need to know exactly which scene belongs to each individual loading operation / Coroutine the moment the loading has completed so that way the transitional VFX can begin playing on the target Stage / Scene.
The issue is — at load-time, the only information I have about which Scene is being loaded is the name. Which means that the moment the scene is loaded I only have the name to use to find it — and with a multi-scene setup like the one I am using — a scene name is totally ambiguous.
Now I might be a bit dense, but could you walk me through (a bit more slowly) how a SceneID component will prevent the ambiguity with 100% certainty? I can’t seem to grasp how it would work in my case.
For instance —
Lets say each time a Stage was loaded in a unique ID was assigned to it.
If I did that, then I could know which stage was NEW by taking a snapshot of the “Stage Registry” before the loading operation had begun and then searching through the Stage Registry to find the newID after the loading operation was complete.
However, there is a chance that multiple stages have been loaded during this period when we take the snapshot of the Stage Registry. If that happens, there could be multiple new IDs… So which one belongs to which load operation? No one can say…
And that’s my issue.
Plus, frankly, I feel like Unity should provide a direct way to get the loaded scene from an AsyncOperation.
Why should I be jumping through all these hoops to get something that should be provided by default?
Currently there is no runtime scene instance. IMHO If we’re asking Unity to give us that, we should ask for the real thing: give us scene instances with individual IDs and all and not through the async handle, that’s only a band-aid.
Have you tried to unload one of those “scene instances” you have? It is unloading all of the scenes of the same “type”.
I tried this once, when I was playing around the basics and built an endless runner. Loading similar parts in scenes. Then I switched and loaded things in prefabs instead. I generally don’t use scenes anymore for these things, and everything is better since then.
Wait… What… REALLY?
Scenes don’t have instances? I just thought they used a struct and had some internal GUID that wasn’t accessible (sorta DOTS ECS vibes).
I… had no idea that this was the case.
Okay, lets ask for that then.
:c
So what you’re saying is I can’t use scenes for something scenes were like, designed for?
WHAT!?
Why does everything in Unity feel half-baked!?
Well, they designed scenes to have some means to load content. They never intended it to have duplicate scenes at the same time. This is the reason you only can handle scenes by name, path and buildindex.
So IDK, it would be much better, obviously, having scene instances, but at the same time I don’t miss them anymore, I load everything by hand anyway and I have one “play scene”. Basically everything I’m doing have “bootstrap” scene (starting up everything), “menus” scene (where the main menu and pause menu live) and “play” scene (where the game is happening). End of story. I load menus and play stage additively from bootstrap. And then handle all load either myself or nowadays through Addressables.
Fair points, but you understand my hyperbole.
Right now I’m just a little shocked that unity did 95% of the work yet again, but because they left off the 5% we are now stuck with another half-baked implementation of something that should’ve been fool proof.
Well I guess anyone can get used to anything — but to an outsider it still seems preposterous.
I generally try to stay away from game-bootstrap scenes.
They’re just too much hassle and they make iteration more difficult because of annoying scene dependencies.
Instead I vied for MonoSystems — which are special MonoBehaviours that automatically instantiate an instance of themselves before the game loads and persist until the game quits.
Really useful stuff.
Anyway,
So I have been doing some digging around and at it seems a Scenedoes have a GUID.
It also has something called a “Handle”… whatever that is used for, I do not know.
public struct Scene
{
internal enum LoadingState
{
NotLoaded,
Loading,
Loaded,
Unloading
}
[HideInInspector]
[SerializeField]
private int m_Handle;
public int handle => m_Handle;
internal LoadingState loadingState => GetLoadingStateInternal(handle);
internal string guid => GetGUIDInternal(handle);
//
// Summary:
// Returns the relative path of the Scene. Like: "AssetsMyScenesMyScene.unity".
public string path => GetPathInternal(handle);
//
// Summary:
// Returns the name of the Scene that is currently active in the game or app.
public string name
{
get
{
return GetNameInternal(handle);
}
set
{
SetNameInternal(handle, value);
}
}
//
// Summary:
// Returns true if the Scene is loaded.
public bool isLoaded => GetIsLoadedInternal(handle);
//
// Summary:
// Return the index of the Scene in the Build Settings.
public int buildIndex => GetBuildIndexInternal(handle);
}
That was only a tiny portion of the Scene struct.
So now I am super confused.
Anybody here got source access? I can’t get any deeper without it.
What exactly is a Scene’s GUID used for?
Unity starts up and acquires all OS resources, sets everything in motion, etc.
When it’s all done and ready, by API convention Unity loads scene zero of your project.
That’s IT. That’s the entire boot process. (Simplified greatly of course)
Beyond that, it’s up to you to have scene zero contain MonoBehaviours in that scene zero, from which your entire custom game behavior arises.
Any mono decorator fire up stuff isn’t guaranteed to happen at any point that Unity can be ready for.
But Unity has equivalents like RuntimeInitializeOnLoadAttribute and whatnot.
When in Rome …
ALSO, as to instancing stuff over and over in a scene, my Pilot Kurt game has 100x100 city blocks that I got off the great Imphenzia Discord channel, and they are all contained in a scene. When my level loads up, those blocks are Instantiated again and again (or not) randomly to build out a city. Lurk’s technique for an endless runner could easily be done that way: have a scene (or scenes) with chunks in them, then find them all, set them all inactive, and start cloning, activating and destroying the instances as you go along.
Yeah, at the time I thought about this option, but since I needed to learn Addressables and wanted my own framework to handle load anyway I went with the more complex option at the time, but you know, all developer write these things usually once.
There are a myriad of instances where initializing system-level Singletons that are totally independent of a bootstrapping scene is completely acceptable and even boost productivity while using those systems in a project.
Are there times where the approach is not recommended? Absolutely.
I wouldn’t recommend the pattern if you plan to load the system differently according to a serialized piece of data you assigned to (presumably) a random ScriptableObject that is magically found in your project.
That sounds iffy.
And Unity understands that your game might also need to “set things in motion” too, which is why they provided the RuntimeInitializeOnLoadAttribute, which you mentioned.
This sounds like a pretty handy way of creating an asset database — I’ll remember this technique in the future. Thanks!
While I know this statement is dang true, I still feel like Unity should try to actively mitigate the need for everyone to make a custom version of everything.
How has your experience been with Addressables?
I considered diving into them a few times, but each time I look at the Addressables forum it seems like everyone agrees there’s a lot of bugs, a lot of deprecated APIs, and some painful workflow problems .
I think it is a bit overengineered, but I can see why, it solves everyone’s problems. So it needs to be complicated.
Well, average Unity forum users like to complain. But joking aside, I’m using it only to load up resources from disk. Nothing more, I don’t do IAP, I don’t do OTA updates, nothing really complicated. All my resources are present at build time. I also don’t really do 2D, so I don’t really care about those things either. Also since I don’t want to halt the entire engine just to load a piece of file I don’t care about the sync API, only the async.
It works for me, I flag my resources, group them, it builds into bundles, loads in build. That’s it.
Scene names can be ambiguous, and therefore unreliable.
However, after a friend provided me with some info — scene-name ambiguity is the least of my problems.
I was going to suggest something similar, though I use a scriptable object to wrap around a scene addressable asset reference, which also keeps tabs of whether the scene is loaded, and handles loading/unloading the scene too.
I’ve never tried, but I wonder if multiple of these objects pointing to the same scene asset reference could be used to load multiple of the same scene? Or maybe CreateInstance to make a duplicate.
While I’m here (and since Unity Scenes seem to have a lot of unspoken caveats), what are the workflows like for using prefabs as Scene replacements?
And for the record, I’m gonna call them Scenefabs.
Some Pros for Scenefabs off the top of my head are:
Can contain custom Scenefab related data.
Said custom data can be accessed without needing to instantiate the Scenefab.
Has no limitations on how many instances of the Scenefab can exist at any given time.
Inherits all prefab related characteristics, such as variants.
Still allows Scenes to be used, presumably in a non-additive way.
Has a cool name.
Some Cons for Scenefabs off the top of my head are:
Can only be loaded synchronously unless using Addressables.
Could be slow to load synchronously — particularly for large Scenefabs*.*
Slightly more unwieldy to author and manage, presumably.
Totally not the “Unity way” to do things. (BUT WHO CARES?)
Are these Pros and Cons accurate reflections of your experiences with using scenes as prefabs?
Is the biggest downside (synchronous loading) completely mitigated by Addressables?