Main question: Why unity unpack completely prefab into scene while building? What’s the purpose of it, why it doesn’t hold it as reference?
Situation/how to reproduce: I have 40 scene with same one big prefab(1mb) with some environment. Each scene file has 150 kb size. But during the build process scenes becomes bigger (1mb + 150kb), and build size increases a lot. I expect 40150kp + 1mb, but receive 40(150kb + 1mb).
I know few ways how to avoid/solve this(create env prefab in runtime, addressables and so on), but i want to understand purpose of prefab unpacking
Simply: a prefab in a scene is an instance of the prefab. It is not “unpacked”, but nevertheless serialized to the scene in its entirety because any part of the instantiated prefab could be changed in the editor.
This requires serializing the prefab to the scene, otherwise any changes made to the instance in the scene (that is not applied back to the prefab) would be lost if the build would only store a reference to the original prefab. In fact, the editor constantly serializes objects in their entirety whenever making changes. Even though you don’t see it, just dragging a slider in Inspector would cause at least the affected component to be thrown away and replaced with a new instance (figuratively speaking, not sure if that’s literally what happens).
In cases like yours the recommendation is to NOT create big prefabs but rather create a scene with the prefab’s content in it. You can additively load scenes to “add” them to a running scene, and this also allows you to load that content asynchronously too.
Prefabs are purely an editor concept in Unity and all prefab handling happens exclusively in the editor. During a build, prefabs are completely unpacked. Links between prefab instances or variants and their prefab assets are completely lost. You’ll note that anything for working with prefabs, like PrefabUtility, is in the UnityEditor namespace.
This has always been the case in Unity and it wasn’t changed with the big nested prefabs overhaul many years ago. Maybe things would be done differently today but I think the reasoning behind it is simplicity – and possibly performance. When a build needs to load a scene, it can a do so in a simple, mostly linear operation. Reading through the scene data, creating all objects, and then linking up references. If prefabs were preserved, it would have to look up and process prefabs, making loading more complicated and possibly slower, due to having to jump around in memory.
As CodeSmile pointed out, if you have many instances of a prefab, you should not keep any redundant data inside of the prefab. That data will be duplicated in each instance. Instead, put the data into another asset, like a scriptable object. Everything will reference the same asset and the build will only contain one copy of it.
I don’t think that’s true? If you look into a scene file containing a prefab, the file contains a “PrefabInstance” entry with only the local modifications. For any parts of the prefab that are referenced by other scene objects, it also contains proxy objects that those scene objects link to. But the scene does not contain the entirety of the prefab.
May or may not be the case, I’m not certain either. What I did notice is this: Some popular asset developer provides a “modular character” asset with a script that lets you configure or randomize the character. Problem was: every configurable part was in that same prefab!
So when I randomly created variations of these characters and instantiated them into the scene, the scene file got bloated to several hundred MB rather quickly.
It required removing all the parts that weren’t set as active in order to reduce the bloat, and also to keep performance in check (editor as well as Get/Find X in children and such). That also meant the characters couldn’t be further modified as easily if I had ever felt the need to do so.
I was surprised that such a system wasn’t engineered, like you hinted at, by selecting one of the meshes from a SO (one list of meshes per moddable type) and just assign a mesh to the existing part’s mesh renderer.
Interesting. I suspect the bloat was coming from a big amount of prefab modifications. The randomization logic likely modified a lot of properties on the instance, maybe also causing many properties to be overridden even if the value didn’t change.
In the scene file, a modified property uses many times the storage than the property itself*. It’s good to avoid having lots of instance modifications or at least to clean them up to the necessary minimum. Tracking and managing all those overrides probably also slows down the editor.
(* Every modification has a target reference with a full File ID and GUID, a property path string, as well as both value and objectReference fields. A normal property on a script, on the other hand, only stores its name and value.)