I have some UI assets that aren’t used all the time, so I would like to dynamically load and unload them using Addressables.
These UI assets refer to various ScriptableObjects storing my game state.
When I load the Addressable objects, it looks like they are creating new instances of the referenced ScriptableObjects - so they don’t share the game state.
Is there a way to tell these dynamically loaded components to all use the same (preloaded) instance of each SO?
I will preface this by saying I have no idea, but that use case kind of seems like its against what addressables is for.
If you need a universal game state that is populated by something from a bundle you should probably load up your scriptable object at the beginning and save the reference to it, then anything that needs to interact with it should target the references object.
My universal game state is not populated from a bundle. I have some scriptable objects that live on the filesystem and are accessed by various scene objects.
The issue is that if you take one of those scene objects and make a prefab out of it, and then instantiate that prefab using addressables, it seems the reference in the instance points to a new copy of the ScriptableObject - not the common one shared with the rest of the app.
@TextusGames Not sure I follow. I’m looking for some way to map the addressables to an existing ScriptableObject which is not an addressable (currently).
@unity_bill I see you have posted replies to a few similar threads.
Can you please chip in here? I don’t want two copies of the scriptable objects - ideally, I want all my addressables to reference the original instances of the scriptable objects referenced by all the scene objects - not create a new instance of each for the addressables world.
Using scenes (regular scenes, not using addressables) work in this way. If I reference the same scriptable object in two separate scenes, the data is shared between them.
Is there a way to get the addressables for a given key to point to the original instance of the scriptable object?
At some point I thought that using scriptable objects for shared states was nice to have but together with addressables it is a bit of a hell due to new instances of the scriptable object. So instead of sharing states using scriptable objects I reverted back to static singleton classes that contain the state. Since most of it is singleton anyway.
So what I now tend to do is use scriptable objects strictly for copies of data only.
i.e. if you have stats for a monster → load in the scriptable object stats → copy stat values to the instance of the monster → unload the scriptable object.
If converting references to static references is not possible or too much work, one workaround would be loading in the scriptable objects at the start and have any other object then load in the scriptable object by AssetReference as well.
As long as the scriptable object isn’t unloaded it should get the same reference.
I had some trouble with that as well at some point. Now I just refrain from using scriptable objects for states at all costs as a result.
I had similar problem with ScriptableObjects. Then I had one Global scene, which is supposed to always exist. It was a non-addressable scene, which loaded all the other Addressable scenes. As Global scene used the same Scriptables that were used in Addressable scenes, Scriptables were duplicated. I fixed this by turning Global scene into addressable too. Now I have one non-addressable GlobalLoader scene in Build Settings, which loads Global, and have no problems with duplicate Scriptables anymore.
Problem with Scriptables appeared once more when I turned Prefabs into addressables. I then had 2 Groups, one with scenes, another with prefabs. Then I ran “Check Duplicate Bundle Dependencies” rule in Analyze Tool and created separate Group with dependencies that were previously duplicated in scenes and prefabs groups.
Yes, that does sound very similar to my situation.
Interesting. I’ve not done anything with scenes via Addressables - how do you tell Unity to fire up the first scene via addressables - usually that’s just part of the build settings?
Fundamentally, I don’t really understand why there is a need for these extra instances in the first place? This all feels really counterintuitive. When I mark a Scriptable Object as addressable in Unity Inspector and give it a key, I expect that instance to be assigned the key, not a copy of that instance.
@unity_bill why does Addressables make this copy in the first place? Is there a way to force the addressables system to assign the key to the same ScriptableObject instance as the rest of the app? If there were, I wouldn’t have to worry about all this stuff, and I could refer to the same instance via an AssetReference in the loaded assets.
GlobalLoader is now the only scene in Build Settings. In Built game Unity loads it. Then GlobalLoader loads Global scene, and immediately GlobalLoader is unloaded. Afterwards I work only with Addressable Scenes. That’s how it is if I understood your question correctly.
So, is GlobalLoader just the name of your bootstrap scene, with your loader as a monobehaviour in that scene? Or is GlobalLoader some part of Addressable Assets I’ve not come across?
I had a similar problem, using ScriptableObjects all over the place, following Ryan Hipple’s great presentation, and found that all addressable SO’s were being duplicated, voiding the hole purpose of addressables if you ask me.
I didn’t find a suitable solution out of the box I’m afraid. I had to build a complex system of initialisation. I ended up using AssetReferences, but this didn’t solve my problem either, as being asynchronous, I had no way of knowing easily when a reference was available to use, without flooding my code with tonnes of annoying code to check and get if not ready. In the end, I created a system based on marker interfaces. Every Monobehaviour (and some others, but that complicates it even further) that has an AssetReference to a SO implements this marker interface, and references the SO through a wrapper class. Then, at startup, a loader object is responsible for going through all objects that implement the marker interface and resolving their asset references. The final, and most annoying part, is that then all objects with any Monobehaviour that has these marker interfaces has to start out disabled, otherwise startup code that relies on the availability of the SO might run. I’ve done this by putting everything under a “Game Controller” object, that I turn off, and if I turn it on to do some editing, and forget to turn it off, everything goes pear shaped when I play, so I’ve gotten used to noticing that weird behaviour.
It’s a real pain, and I wish I hadn’t had to spend so much time on something that seems like it should just work, but hey, onwards and upwards. The advantage is, I can use assetreferenced SO’s throughout my code, without concern for whether they have been retrieved or not, and be quite safe in the knowledge that, as long as I follow the arcane rules, it’ll work. Fingers crossed.
@Paul_H23 Spot on. Yes, that’s pretty much exactly where I am. My approach is also based on Ryan Hipple’s presentation from Austin 2017. It works wonderfully - until you try to use addressables.
I experimented with AssetReferenceT a bit - hit the same asynchronous issues.
My use case is specific: to dynamically load/unload parts of the app to conserve memory. It seems like a perfect fit for addressables, but as everyone is saying it’s incompatible with the Richard Fine/Ryan Hipple approach, so I may have to go with small scenes loaded additively, each scene only containing a few prefabs.
For me, the other option does seem to be to use addressables but maintain my own ServiceLocator style map of ScriptableObjects to address and replace the duplicate entries - but that seems messy and goes against the grain of the whole Dependency Injection architecture I’m using.
Like you, it feels to me like this is something addressables should address “out of the box” - especially since we are both following well respected (and Unity promoted) architectural guidelines. Is there anyone else over at Unity I should be asking about how to approach this?
There are many ways around it. But you should pick something that you are comfortable to work with.
After I experimented heavily with ryan hipple’s approach, at first I found it nice to just reference the scriptable objects but once the project grew larger and larger it became a hassle. Managing that many scriptable objects is not great at all.
When implementing addressables it became even more of a hassle due to duplicate instances of the scriptable objects.
At that point you have a few options:
Get rid of the scriptable objects and replace them with static class entries i.e.
Preload scriptable objects and reference them in a list somewhere so that additional loads take that same instance of the scriptable object. (that’s how it should work but not certain)
Initialize them via a higher up management system to make sure that the scriptable object is of the same instance.
Something like a dictionary which takes an asset reference as key and keeps the instance as value.
When the key doesn’t exist in the dictionary you load the instance and add it to the dictionary.
It’s hard to tell what to do without knowing your project. People can only give you some workarounds, but thats about it.
In my case I just got rid of most scriptable object variables / references and replaced them with static class entries.
Some of them I didn’t replace and loaded them in in a bootstrap scene and then loaded the reference additionally in other scripts.
Basically learned my lesson to use scriptable objects for data only and not for coupling systems together.
When projects become pure DOTS based then this doesn’t matter much anymore as everything is DoD based and not OOP based.
Hi @MaskedMouse , well I can only speak from personal experience of course but over here the Ryan Hipple approach works well. I don’t want to use singletons because that statically binds relationships - you need to change code to remap a reference to a subtype. Creating hard references between entities like this makes it difficult to test, isolate problems or evolve the design over time.
With scriptable objects, the whole system is mapped out via inspector-defined relationships. This is a fundamental principle of inversion of control and one of its main benefits. Having worked on a few large projects like this, I have found it scales very well. I’m not sure what the hassles were that you experienced - but linking scriptable objects via the inspector was straightforward for us; it’s been easy to swap out a single instance (or multiple instances).
For these reasons, I don’t see removing scriptable objects as a solution - they are not the problem, in our system at least.
Most ways of using addressables with scriptable objects seem to involve a workaround - either a dictionary to preload & “fix” the references at instantiation time or using the service locator pattern possibly via AssetReferenceT. Both solutions lose this fundamental advantage of the Ryan Hipple / Inversion of Control approach (i.e. it’s better to link components in just one place, in Unity Inspector).
Again, I would side with @Paul_H23 to suggest this feels fundamentally like an issue that should be addressed in Addressable Assets. Scriptable objects are supposed to work the way they do everywhere else in the Unity stack. Of course, that’s a design decision for the folks over at Unity. For our specific use case (remove assets dynamically to conserve memory), changing the whole architecture to use Addressable Assets doesn’t seem to be the right trade off.
If there is no way to assign the original instance of each scriptable object to a single Addressable Assets key, the best solutions for us are probably back to basics:
use prefabs and instantiate/destroy them directly
or
use small scenes to group prefabs and instantiate/destroy those directly