Modifying a loaded scene before it's live

I’m trying to get round a problem I encountered when combining Ryan Hipple’s excellent ScriptableObject talk and the new AddressableAssets system. I use variables all throughout the game, variables defined as ScriptableObjects and stored as assets in the system. Once I moved to an AddressableAssets based approach to asset management, due to the need to implement backend based updates at some point, this all got a bit messy. ScriptableObject assets seem to cause an immense amount of pain and confusion when combined with the AddressableAssets system, invariably resulting in cloned versions all over the place, defeating the point of the ScriptableObject variable approach. The only solution I’ve found is to make every use of a ScriptableObject variable member an AssetReference, then I’m left with the problem of how to load them effectively before using them, especially given the asynchronous nature of the AddressableAssets system. Simple example, if I have a control variable that defines scrolling speed, in an asset, that is loaded asynchronously, it becomes a nightmare to control any code that relies on that variable to ensure it only gets executed once the asset is loaded, especially things like Start and Update.

I have a system that I hope will help to resolve this, a combination of introspection, marker templates and pre-processing code that will allow me to centralise the resolution of any variable assets required by a loaded asset such as a scene, and report via promises when they are all resolved and the main asset is therefore ready to be used. This works for loaded a prefab for example, so if I have a prefab that represents an element of the game, I can load it asynchronously via AddressableAssets, and when it’s ready run over the contents and resolve any variable asset dependencies myself, all good. However, this breaks down catastrophically when dealing with a scene.

I use AddressableAssets.LoadSceneAsync from a barebones bootstrap scene to load the main game scene, setting activateOnLoad to false. When I hit the handler I assign to the Completed event, it seems that the scene isn’t loaded, and until I call Activate() on the SceneInstance, it won’t be loaded, ever. However, at this point, rootCount is zero, and only becomes something other than zero when Activate() is called. Unfortunately, after Activate() is called, Start and Update are called on a variety of items within the scene, which as you can imagine causes havoc as the variable assets are not resolved yet.

Long story short, I want to load a scene in the background, not have it “activated” (whatever that really means in this context), and be able to traverse the scene elements resolving dependencies, and then, when I’m happy that it’s all good, active the scene and switch to it, is that really too much to ask?

“…backend based updates at some point…”

What exactly do you mean by this? I’ve not had the problem of duplicating ScriptableObjects all over the place- are you changing their values programmatically with a running scene or something?

No, I was referencing them in assets that are addressable, referencing them as normal references. When I built the player content, and ran in “Packed Play Mode” I found that (using the debug inspector) at runtime there were different versions of the ScriptableObjects in each case, which I couldn’t track, and was unpredictable, I can’t have things that are unpredictable, so I decided to avoid letting AddressableAssets do what it thinks is right without informing me in any detail, and instead make everything a reference, so that I can absolutely control it, and know that each SO referenced in any part of the game, whether that be built into the player or loaded from an assetbundle, would 100% be referencing the same SO asset.

I think I may have come up with a workaround, still very keen to hear if anyone has any better ideas, but in the meantime this seems to work for my use case.

In any scenes that are to be loaded and processed in this way, create a single root GameObject to hold everything else in the scene, or at least everything that will interact in some way with asset based variables, and make it inactive. Then, on loading I can activate the Scene, even load it additively, process all the scene objects and resolve the dependencies, and only when that is done, SetActive(true) on the singular root object. This prevents unwelcome calls happening before I have done what I need to do to the scene, and seems to work ok so far, fingers crossed.