helping you make
sense of your assets
since 2013

poor mans nested prefabs


by Nicholas Francis
Sep 12, 2013

One of the things we’re sorely missing from Unity is nested prefabs. So we rolled this little script to help us out – and figured other people could benefit as well.

You just add this script to a gameobject, and point the prefab field at the prefab you want instantiated.

This will render the prefab in the scene view while editing, and at bake time it will copy the objects into the scene so your gamecode can just work. It doesn’t allow overriding properties, and only previews the meshes, but we’ve been able to use it a lot for building props: We have a Gameobject that contains all the logic for a prop (e.g. definitions of cover points for a crate) – and then uses this script to pull in the actual FBX file that has the graphics. Now our graphic artist can just modify that and everything works.

You can also reference other prefabs, and nest these PrefabInstance scripts. While not as cool as a real system, it pretty much has solved our use cases.

using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.Callbacks;
#endif
using System.Collections.Generic;

[ExecuteInEditMode]
public class PrefabInstance : MonoBehaviour
{
	public GameObject prefab;

#if UNITY_EDITOR	
	// Struct of all components. Used for edit-time visualization and gizmo drawing
	public struct Thingy {
		public Mesh mesh;
		public Matrix4x4 matrix;
		public List<Material> materials;
	}

	[System.NonSerializedAttribute] public List<Thingy> things = new List<Thingy> ();

	void OnValidate () {
		things.Clear();
		if (enabled)
			Rebuild (prefab, Matrix4x4.identity);
	}

	void OnEnable () {
		things.Clear();
		if (enabled)
			Rebuild (prefab, Matrix4x4.identity);
	}

	void Rebuild (GameObject source, Matrix4x4 inMatrix) {
		if (!source)
			return;

		Matrix4x4 baseMat = inMatrix * Matrix4x4.TRS (-source.transform.position, Quaternion.identity, Vector3.one);
		
		foreach (Renderer mr in source.GetComponentsInChildren(typeof (Renderer), true))
		{
			things.Add(new Thingy () {
				mesh = mr.GetComponent<MeshFilter>().sharedMesh,
				matrix = baseMat * mr.transform.localToWorldMatrix,
				materials = new List<Material> (mr.sharedMaterials)
			});
		}

		foreach (PrefabInstance pi in source.GetComponentsInChildren(typeof (PrefabInstance), true))
		{
			if (pi.enabled && pi.gameObject.activeSelf)
				Rebuild (pi.prefab, baseMat * pi.transform.localToWorldMatrix);
		}		
	}

	// Editor-time-only update: Draw the meshes so we can see the objects in the scene view
	void Update () {
		if (EditorApplication.isPlaying)
			return;
		Matrix4x4 mat = transform.localToWorldMatrix;
		foreach (Thingy t in things)
			for (int i = 0; i < t.materials.Count; i++)
				Graphics.DrawMesh (t.mesh, mat * t.matrix, t.materials[i], gameObject.layer, null, i);
	}

	// Picking logic: Since we don't have gizmos.drawmesh, draw a bounding cube around each thingy
	void OnDrawGizmos () { DrawGizmos (new Color (0,0,0,0)); }
	void OnDrawGizmosSelected () { DrawGizmos (new Color (0,0,1,.2f)); }
	void DrawGizmos (Color col) {
		if (EditorApplication.isPlaying)
			return;
		Gizmos.color = col;
		Matrix4x4 mat = transform.localToWorldMatrix;
		foreach (Thingy t in things)
		{
			Gizmos.matrix = mat * t.matrix;
			Gizmos.DrawCube(t.mesh.bounds.center, t.mesh.bounds.size);
		}		
	}

	// Baking stuff: Copy in all the referenced objects into the scene on play or build
	[PostProcessScene(-2)]
	public static void OnPostprocessScene() { 
		foreach (PrefabInstance pi in UnityEngine.Object.FindObjectsOfType (typeof (PrefabInstance)))
	    	BakeInstance (pi);
	}

	public static void BakeInstance (PrefabInstance pi) {
		if(!pi.prefab || !pi.enabled)
			return;
		pi.enabled = false;
		GameObject go = PrefabUtility.InstantiatePrefab(pi.prefab) as GameObject;
		Quaternion rot = go.transform.localRotation;
		Vector3 scale = go.transform.localScale;
		go.transform.parent = pi.transform;
		go.transform.localPosition = Vector3.zero;
		go.transform.localScale = scale;
		go.transform.localRotation = rot;
		pi.prefab = null;
		foreach (PrefabInstance childPi in go.GetComponentsInChildren<PrefabInstance>())
			BakeInstance (childPi);
	}

#endif
}

It uses the same principle as our IBakeable interface, but we want this to run before all the other IBakeable functions (so all objects are instantiated before we try to run others)


by Corey
September 12, 2013

Called out by Nich! Love it! Keep us on our toes.


by Benoit FOULETIER
September 16, 2013

OnValidate()?


by alex
September 17, 2013

@Benoit asking myself the same o.o


by Nicholas Francis
September 18, 2013

OnValidate is called after an inspector modification. Not documented up to 4.2, but has been there since Unity 1.0, IIRC

It’s super useful :)


by Andrew Lukasik
October 2, 2013

This is super-useful functionality thank you for sharing it!

I hope that some day in Unity we will see something as good as prefab nesting system from Monolith’s Jupiter EX years ago. I totally loved it because it was enabling amazing stuff to be possible like for example: many people working on the same level simultaneously (with every person having their own master-prefab like “level-1 lighting (John)” or “level-1 gameplay (Bob)” – it was really awesome).


by Andrew Lukasik
October 3, 2013

Line 41. gives me casting error when instiantiating certain prefabs. I changed it to “foreach (MeshRenderer mr in source.GetComponentsInChildren(typeof (MeshRenderer), true))” but I’m not sure is it ok that way (not a pro coder).


by Mike Diskett
October 4, 2013

We are also making a Cyberpunk city game (Satellite reign) but for desktop. This is an awesome solution thanks for sharing…


by Trond
October 11, 2013

This is pure gold! Not only does it allow for nested prefab-functionality, but it will also help keep text-based scene files manageable (i.e. no more dumping the entire prefab hierarchy to the saved scene)! I will definately make use of this, thanks for sharing!

@Andrew Lukasik: Your fix seems to be spot on. The script could also use an additional loop handling SkinnedMeshRenderers.


by Janus
November 17, 2013

Looks very useful! However as a scripting noob I cant really figure out how to get this working. I have made this thread on the unity forums; http://forum.unity3d.com/threads/211431-Poor-mans-nested-prefab
Any help as to exactly use this script would be greatly appreciated :)


by Jonathan Ryan
January 3, 2014

Many many thanks for this script. It works great and will save many hours.


by Andrew Lukasik
March 8, 2014

Hi. I have trouble baking navigation meshes; is there any known fix/workaround to this?


by Patrick Hogenboom
March 28, 2014

Finally got round to playing with this script, so glad I did!
Hats off to you, this is a sorely missed piece of functionality in Unity.
Thanks a lot for sharing.


by Patrick Kokai-Kuun
May 9, 2014

@Andrew Lukasik – I was having a similar issue and i realized it was due to one of my prefab references having a sprite renderer attached, I replaced the sprite renderer with a simple plane and it solved the error. Hope that helps


by Julian Erickson
July 14, 2014

This code works great and I love it! Thanks for this! I happened to notice something that was giving me trouble though. The section that deals with “MeshRenderer” had an issue when another kind of “Renderer”, such as the particle system, was in the prefab. I fixed the problem by replacing the term “Renderer” with “MeshRenderer”. I just thought I’d give my constructive feedback.

Yet again, thanks a lot for this golden script!


by Steeve Blanc
August 6, 2014

Hi. Thanks so much for sharing a free nested prefab solution.
I have trouble using it with 2D sprites because there is a SpriteRenderer instead of a MeshRenderer, thus there is no mesh, no texture, the gizmo cube is not allowed and other issues …
I failed at refactoring the script for it works with SpriteRenderer, but could you please do it? It would be so useful for Unity 4.3 and+
Many thanks


by Nicholas Francis
August 29, 2014

@Julian Erickson: Thanks. I’ve updated the script

@Steeve Blanc: Sorry, I just don’t have the time.

@Andrew Lukasik: We’ve run into the same issue. Our solution was to make a custom NavMeshBake script, that found all prefab instances, made them spawn the prefabs, asked Unity to NavmeshBake, then cleaned up after itself.

It’s kind of a hassle, and the script has tons of other dependencies, so it’s not easily pulled out :(

Add Your Comment