I have huge customization with lots of prefabs of different sorts - models, particle systems and so on. I manage them with a few scriptable objects: ItemsMaster with an array of ItemsCategories with arrays of prefabs themselves. Player and NPC’s require a ScriptableObject called Skin, which consists of items, one for each category. So now I want these Skins to be persistent between game sessions.
The general question is “How do I save a reference to a prefab and load it at runtime”. I currently see one possible solution:
Some object at the scene storing all the prefabs in an array. I can use ItemsMaster as a tree, use ItemsCategories as nodes, and store the path to a prefab like a combination of arrays indices.
I’m afraid that an object at the scene full of references to prefabs forces unity to load them all. Is this true in the case of ScriptableObjects with references to other ScriptabeObjects with references to prefabs? How dangerous is this?
I don’t want to use Resources because of hard management - changing names or paths require to make manual changes somewhere else. I’m not familiar with Addressables and asset bundles, but I think it will require a lot of effort from me to begin using them now.
Anything referenced from a scene, anything that stuff references, and that stuff references, etc, etc, is all loaded into memory when the scene loads. Not sure what you mean by dangerous. If you’re loading more stuff you’re going to take longer to load the scene. Unity has had a 4GB limit on assets referenced in a single scene, but I thought I read they were finally working on a fix for that. The same limit applies to the Resources folder. (A work around for that limit has been to spread assets you need to load across multiple scenes, even if you’re really only going to use them all in a single scene). That’s really the only danger I can think of.
So what’s the best way unity offers to deal with shops or customization systems where a huge amount of prefabs can take place? Loading all items from the shop in memory is a bad way, especially on mobile, and leads to long loading times and unwise memory usage. How do I reference prefab without loading it? (without Resources and manual string/path hard-to-use references if possible)
Using assetbundles is a good way to load assets when you need them.
Also the Addressable Asset System seems like a good solution to manage the loading.
Would like to point out that I have hit a really hard wall using ScriptableObjects that reference Prefabs, Scene paths and others, similar to how you intend to use it.
On my side, just loading the application on the very first main menu welcomes a ton of assets preloaded even though they are not used in the current minimal Main Menu scene (this one is just a scroll list with buttons to load actual the scenes).
Doing a Memory Sample (Detailed version) with the profiler shows that the scene itself is minimal but the assets loaded in memory is huge, incredibly huge. I don’t know yet what that ‘other’ category even bigger memory footprint is yet.
So, you might want to double check and be careful with that, I don’t think this is fixable except by using either AssetBundles, Addressables or manual Resources.Load. Anything referenced by an inspector link looks like it will load it no matter what if it finds it’s way to the asset via SOs and/or Prefabs references.
It does get worse with nested prefabs, on the ‘Assets’ category there, there’s a ParticleSystem section littered with thousands tiny particle systems instances each of no more than a few KB, but together amount to a whopping 65MB. It’s like if a particle system prefab is referenced on another prefab and then variants and variants of variants it will create a ton of permutations of it. To clarify, this is without even loading the scenes themselves that contain the particle systems yet.
On that note, if anybody has any idea of how to go about it or what to do for this, it would be greatly greatly appreciated. I don’t even know if this is a bug or the intended behaviour.
That is correct and intended behavior. These references are built into reference trees that inform, at build time, what assets need to be loaded into Memory for a particular scene or an object loaded by any other means. On loading it, these referenced things are immediately available for e.g. scripts to act on them so they need to be in memory.
If you want to see these reference chains in more detail, the Memory Profiler package provides a better way to follow through the references (and will get an even more straight forward way to do so rather soon).
If you want to have more fine grained control over when what is loaded, you need to add a level of indirection to the references, which currently mostly means string/scene IDs references instead of GUID assignments I the editor. Be that via Addressables, AssetBundles or by splitting things up into scenes that are loaded additively.
Thanks a lot for the prompt reply. It makes a lot of sense.
Yes! The Memory Profile package is amazing as of now, if it’s going to get enhanced all the better. I do take snapshots, diff’em, etc… however for example the particle systems (expanded below), there’s 5000 copies of them, it gets hard to go on the snapshot themselves, however, as an overview it excels already. Great great tool.
The only head scratching part for me then is regarding particle systems, there are maybe 50 prefabs total but used on references, scenes or inside other prefabs always as prefabs themselves, in many many places though. What’s the reason for creating instances of the same particle system in memory?
Is this normal or intended behaviour (before heading on a potentially long hunt). The size is so tiny for each though that feels more like a managed wrapper around it more than anything else.
you’d have to unfold this for details but part of that will be Exectuable and DLLs, i.e. purely the executable program logic.
Regarding the Particle Systems in nested Prefabs… I’m not to firm in how this works with nested prefabs and variants but: Managed Wrappers only exist in a built player if you have a script that holds a reference to it. The Memory Profiler Package can show you if are looking at the underlying Native Object or at a Managed Object with a Native Object underneath it (i.e. the Wrapper). You should only ever be getting one managed wrapper per native object, no matter how many references you have to it, so I kinda doubt it is a wrapper but more likely those are all their own, genuinely separated Native Object.
And I mean, it probably makes more sense if you think about it like this: every particle System in your scene will need to keep track of the particles in it, its values and current state. They can all share the material and shader for displaying purposes but the individual effect in the scene is not an asset like a mesh would be, loaded once and referenced multiple time to then get rendered. Mesh Assets wouldn’t need to maintain their state in a way a Particle system has to.
Now Mesh Renderer components you could have multiple of for the same mesh. The GameObject it is on is a Native Object and together with the Transform component and others defines how and where that Mesh gets rendered. So the Particle System Component is, well, a component, and not a pure data asset like a Mesh.