Stupid Prefab Tricks

As I work on my Project Search & Replace tool I get to learn a lot about the internals of Unity. I’m currently adding the ability to search and replace prefab instances to the tool(edit: now out in v1.1!), and I’ve also written a free utility for prefabs (scroll down to the Prefab Hierarchy Inspector or check it out in the code archive). We are going to go deep into some un(der)documented areas of Unity. Are you ready?!

Prefab Internals

prefabs_how_do_they_work

The Prefab Class

For clarity I’m going to refer to instances of the Prefab class ‘proper’ as a ‘Prefab class’ instead of just ‘a prefab’. Internally the Prefab class is a class that extends UnityEngine.Object, similar to all other UnityEngine.Objects. This class is not documented (but it is mentioned and used in PrefabUtility). It contains:

  • A hierarchy of GameObjects and Components (m_RootGameObject).
  • A list of PropertyModifications (m_Modifications).
  • A list of ‘removed components’ (of little important to this post).
  • A reference to a parent Prefab class (m_ParentPrefab).
  • possibly more…?

Prefabs are not GameObjects, but instead contain GameObjects. They are a special UnityEngine.Object type called Prefab that is returned when you call PrefabUtility.GetPrefabObject(). When you look at a prefab in the Project tab, you are actually looking at the GameObject and Components inside the Prefab class.

Most of this post I gleaned from using my SerializedPropertyViewer. I was looking through Unity Decompiled and I saw these interesting lines in PrefabInspector.cs:

SerializedObject serializedObject = new SerializedObject(prefab);
SerializedProperty serializedProperty = serializedObject.FindProperty("m_Modification");

It didn’t occur to me that you could get a Prefab class and use SerializedObject to get the internal information! Much properties. So wow! So here is some missing documentation on the Prefab class which is accessible via SerializedProperty.

m_PrefabInternal & m_PrefabParentObject

I always noticed GameObjects and Components have two interesting prefab related properties: m_PrefabInternal and m_PrefabParentObject. These exist on all GameObjects and Components. But what are they and what do they do?

m_PrefabInternal is a reference to the Prefab class that the object is a part of. All GameObjects and Components contain a reference to the Prefab Class it is inside. Prefab instances within scenes also have their own Prefab class instance saved in the scene file along side them (more on this later).

m_PrefabParentObject is a reference to the prefab object (not class) that is the ‘parent analog’. For example the Transform of ‘ChildInstance’ points to the Transform of ‘Child’ in the Prefab. This is used along with PropertyModifications to apply the modifications to the instance (more on that later). When you use PrefabUtility.GetPrefabParent() the object within this property is returned.

m_PrefabParentObject is essentially non-editable in my findings, or at least changing it has little benefit.

m_ParentPrefab …and inherited prefabs?

It is worth restating: prefab instances use the Prefab class as well. Saved within the scene file for any prefab instance is a second Prefab class that describes the unique property modifications that override any modifications in the original prefab. This second prefab makes use of a m_ParentPrefab (completely different than m_PrefabParentObject!) property of the Prefab class to point to the original prefab.

Its interesting to note that this design makes it look like inheritable prefabs were part of the original designs for Unity. Of course, why wouldn’t it be? I have written my own prefab systems with multiple levels of inheritance in my own game engines and well, it wasn’t really that hard. But it wasn’t fast either, so maybe that’s led them to not implement more levels of prefab inheritance.

m_Modifications

The last part of the prefab internals to go over are PropertyModifications. The prefab class contains an array of modifications that are applied to the prefab.  For example, a Transform’s x property may be set to ’42’ and the PropertyModification would contain:

  • target – A reference to the Transform of the prefab.
  • propertyPath – m_LocalPosition.x
  • objectReference – null (in this instance)
  • value – 42

As the prefab instance is built, ie. as it creates a new GameObject or Component, it looks at the modifications and determines if it needs to apply a change.

Building a Prefab Instance

Moving forward I will call the Prefab class inside the scene the ‘Instance Prefab’, and the Prefab class inside the project the ‘Project Prefab’.

So how is a prefab instance built when you’re in the editor?

  • Upon opening a scene the individual objects are built.
  • The prefab instances (and classes!) are built as part of this step behind the scenes.
  • Then the scene’s hierarchy or graph is assembled.
  • The editor encounters a game object that references an Instance Prefab.
  • The editor builds out the graph of objects based on the Project Prefab (using the reference the Instance Prefab has to the Project Prefab).
  • After building out the Project Prefab’s sub-graph then all PropertyModifications of the Project Prefab are applied .
  • Lastly all PropertyModifications of the Instance Prefab are applied (there is no sub-graph for Instance Prefabs to build out, only Property Modifications).

Some of this is speculation, so I might have missed some details. I would love any additional information Unity engineers! 🙂

Applying Property Modifications

As the editor creates a Component for the instance it will point the m_PrefabParentObject to the corresponding Component in the prefab. At some point (maybe immediately, maybe later, I can only speculate) it will iterate over the Prefab’s PropertyModifications and check the m_PrefabParentObject and if they match, it will apply the value or objectReference to the instance.

Building a Prefab Instance at Runtime

So does building a prefab instance at runtime work like the editor? Signs point to no. This post on serialization talks about the details:

  • Prefabs. Internally, a prefab is the serialized data stream of one (or more) game objects and components. A prefab instance is a list of modifications that should be made on the serialized data for this instance. The concept prefab actually only exists at editor time. The prefab modifications get baked into a normal serialization stream when Unity makes a build, and when that gets instantiated, the instantiated gameobjects have no idea they were a prefab when they lived in the editor.

So the steps I mention above are applied to the GameObject within the scene and then baked. All Prefab information such as the Prefab class, m_PrefabParentObject, m_PrefabInternal is lost upon baking. There is no concept of prefabs at runtime (and this is probably another reason we do not have nested/inherited prefabs). My assumption is that this is a performance consideration.

Instantiating Prefabs

The serialization post also has some interesting information when using Instantiate():

  • Instantiation. When you call Instantiate() on either a prefab, or a gameobject that lives in the scene, or on anything else for that matter (everything that derives from UnityEngine.Object can be serialized), we serialize the object, then create a new object, and then we “deserialize” the data onto the new object. (We then run the same serialization code again in a different variant, where we use it to report which other UnityEngine.Object’s are being referenced. We then check for all referenced UnityEngine.Object’s if they are part of the data being Instantiated(). If the reference is pointing to something “external” (like a texture) we keep that reference as it is, if it is pointing to something “internal” (like a child gameobject), we patch the reference to the corresponding copy).

So even though the object in the scene isn’t a prefab….for all intents and purposes you can call Instantiate() on it anyway and get the same effect. But you can’t expect to be able to determine if the object is derived from a prefab in the project. This occurs in the Editor as well. If you want to instantiate a prefab in the editor and have it maintain a reference to the original prefab you must use PrefabUtility.InstantiatePrefab().

Stupid Tricks

While this may be academically interesting if you are an engine geek, you may be asking what can I do with this information? Well you can programmatically swap out prefabs and also modify internal prefab values.

Use the following code at your own peril. Code driven by stunt coder on private course.

Getting the Prefab object.

For any UnityEngine.Object within a Prefab or instance you can get the Prefab class like so:

SerializedObject sObj = new SerializedObject(obj);
 SerializedProperty propInternal = sObj.FindProperty("m_PrefabInternal");
 UnityEngine.Object prefabClass = propInternal.objectReferenceValue;

As far as I know it is not possible to cast this object to a Prefab type. The best way to work with this object is to use SerializedObject:

SerializedObject pInternal = new SerializedObject(prefabClass);

Swapping out a Prefab Instance

The propInternal SerializedProperty allows us to modify what Prefab class the object points to. If you do this on the root object of a prefab instance and work a little magic, you have swapped the prefab for a new one!

// 'rootGameObject' being the root game object of a prefab instance within a scene.
SerializedObject sObj = new SerializedObject(rootGameObject);
SerializedProperty propInternal = sObj.FindProperty("m_PrefabInternal");
GameObject someOtherPrefabRoot; //another prefab

// Point the m_PrefabIternal object to the other prefab
propInternal.objectReferenceValue = PrefabUtility.GetPrefabObject(someOtherPrefabRoot);

sObj.ApplyModifiedProperties();

/** Save and re-open the scene and Unity will re-build the scene with the new prefab. **/

But what about PropertyModifications?

The above code points one prefab instance to another. BUT it does not take into account PropertyModifications, meaning that the m_Modifications targets are now wrong. As far as I can tell this does not tragically break Unity, thankfully. So perhaps we could change the target of the PropertyModification to an Object in our new prefab? Why yes…you can!

But…how? Prefabs can have completely different hierarchies, objects could be missing, renamed, or moved. This is a hard problem (one I have solved in Project Search & Replace but I won’t get into that here). First you must somehow ‘map’ the hierarchies of the two prefabs in order to migrate the targets.

But if for example the PropertyModifications are simple, say on just the root transform, it is quite simple to iterate over them and change them:

// Get the array of PropertyModifications in the Prefab class
SerializedProperty mods = pInternal.FindProperty("m_Modification.m_Modifications");

// The following silliness is part of iterating over arrays of things in SerializedProperties
mods.Next(true);
mods.Next(true);
int numMods = mods.intValue;
for(int i = 0; i < numMods;i++)
{
 mods.Next(false);
 // The following code makes the assumption that the only PropertyModifications
 // are on the root transform. Totally example code!
 SerializedProperty target = mods.FindPropertyRelative("target");
 UnityEngine.Object targetObj = target.objectReferenceValue;
 UnityEngine.Object replaceObj = someOtherPrefabRoot.transform;
 target.objectReferenceValue = replaceObj;
}

sObj.ApplyModifiedProperties(); 
/** Save and re-open the scene and Unity will re-build the scene with the new prefab. **/

And now the targets are fixed, and the prefab has been swapped. This method is super complex, but allows for the migration of information between your prefab instances instead of simply overwriting and re-creating it by hand!

Prefab Hierarchy Inspector

While implementing the above code I spent a good amount of time working on prefabs in the Project tab. And while doing this the same recurring thoughts kept coming back to me:

  • Why can I only go one level inside a Prefab?
  • I keep wanting to see the same sub-objects sometimes. Why can’t Unity remember where I was in a Prefab?

So I wrote an editor window that allows me to see the entire prefab hierarchy.

Other features:

  • Arrow keys allows me to navigate the hierarchy.
  • Navigating to a new object selects it in the Inspector.
  • Remembers the current view state in a prefab if I go back to it later.
    • Persists to disk and between sessions.
  • Keeps a list of the ten most recently used prefabs in a dropdown for quick access.

But why does Unity default to only 1 level deep?

From a bit of exploration, it seems that this is a design decision on purpose. This thread on the Unity forums sheds some light:

We’ve limited that to one level for a few reasons:
– A larger level made by an artist can have many, many game objects inside it. Letting people make references to game objects deep in the hierarchy seemed to be dangerous, as it would be too easy to introduce code dependencies on arbitrary art assets.

– In code-based prefabs, its usually more flexible to have some accessor properties in the root game object that has pointers to what you need.

I completely agree that putting accessors in your root object is the way to go. But I don’t know how well this artificial limit really helps that. I shall be using my hierarchy inspector and see if I ended up still dragging stuff into scenes in order to see what I’m modifying.

Serialized Property Viewer

In my explorations of Unity internals I heavily rely on SPViewer, a free tool I have written to view the serialized form of Unity Objects within the editor. In order to view the m_PrefabInternal objects of prefab instances I have updated the code to view UnityEngine.Objects that are internal to other objects (because you can’t drag an Instance Prefab into the editor window).

Stupid Trick…or Treat?

I hope you’ve learned something new about prefabs in Unity. And thanks for indulging  me by reading this far. After this week’s work it seemed worthwhile to document my experience so that perhaps others can find interesting uses for what I have discovered.