Replace game object with prefab?

All these seem so overcomplicated. Here, I’ve taken and optimized it all to work based on what game objects you have selected, rather than having to individually drag an drop every object into an array object in the window… Enjoy.

using UnityEngine;
using UnityEditor;
using System.Collections;

public class ReplaceGameObjects : ScriptableWizard
{ 
	public GameObject useGameObject;
	
	[MenuItem ("Custom/Replace GameObjects")]	
	static void CreateWizard ()
	{
		ScriptableWizard.DisplayWizard("Replace GameObjects", typeof(ReplaceGameObjects), "Replace"); 
	}
	
	void OnWizardCreate ()
	{
		foreach (Transform t in Selection.transforms)
		{
			GameObject newObject = (GameObject)EditorUtility.InstantiatePrefab(useGameObject);
			Transform newT = newObject.transform;
			newT.position = t.position;
			newT.rotation = t.rotation;
			newT.localScale = t.localScale;
		}
		foreach (GameObject go in Selection.gameObjects)
		{
			DestroyImmediate(go);
		}
	}
}
2 Likes

Modified so that parent-child relationships are kept intact.

:slight_smile:

using UnityEngine;
using UnityEditor;
using System.Collections;

/*
 * http://forum.unity3d.com/threads/24311-Replace-game-object-with-prefab/page2
 * */

public class ReplaceGameObjects : ScriptableWizard

{ 
    public GameObject useGameObject;

    [MenuItem ("Custom/Replace GameObjects")]   

    static void CreateWizard ()
    {
        ScriptableWizard.DisplayWizard("Replace GameObjects", typeof(ReplaceGameObjects), "Replace"); 
    }

    void OnWizardCreate ()
    {
        foreach (Transform t in Selection.transforms)
        {
            GameObject newObject = PrefabUtility.InstantiatePrefab(useGameObject) as GameObject;
            Transform newT = newObject.transform;

            newT.position = t.position;
            newT.rotation = t.rotation;
            newT.localScale = t.localScale;
			newT.parent = t.parent;

        }

        foreach (GameObject go in Selection.gameObjects)
        {
            DestroyImmediate(go);
        }
    }
}
2 Likes

Thanks for this! I kiss the ground on which you walk. :slight_smile:

1 Like

Awesome update to the script, works great, thanks man

Just wanted to say thanks for this, saved me a bunch of time.

This is a really useful script. Thanks to everyone who worked on it :slight_smile:

There’s something I’ve been looking to to be able to do in Unity for years now. And this script comes the closest so far…

How hard would it be to automate this process a little more, so it could replace multiple DIFFERENT objects with DIFFERENT prefabs.

eg. I export an FBX from Max that contains hundreds of modular instanced objects (wall sections, foliage, etc). Import it into Unity and put it in the scene where you can see it still has all the individual objects as children under a game object. And I’ve already made all the prefab wall sections, foliage objects, etc.

Could I then run a script that looks at each child object, figures out which of my prefabs it matches. And then go through and replace them all. I could build whole levels in Max this way. It would be amazing!

The instances from Max have a unique number suffix, but other than that the name of each one could be used to find it’s replacement prefab.

Even better would be if I could put this script on an empty game object in the scene and use it like a manager so it can be told which fbx (or selection of fbx’s) from the Assets folder to source and then when you click go it basically runs through the process automatically leaving you with a whole lot of placed prefabs. It could maybe have an option to auto-update if it detects the fbx has been changed.

I would pay big money for this functionality if I saw it on the Asset Store.

Just added some undo support into your script.

using UnityEditor;
using UnityEngine;

/*
 * [url]http://forum.unity3d.com/threads/24311-Replace-game-object-with-prefab/page2[/url]
 * */

public class ReplaceGameObjects : ScriptableWizard
{

	public GameObject useGameObject;

	[MenuItem("Custom/Replace GameObjects")]

	static void CreateWizard()
	{
		ScriptableWizard.DisplayWizard("Replace GameObjects", typeof(ReplaceGameObjects), "Replace");
	}

	void OnWizardCreate()
	{
		foreach (Transform t in Selection.transforms)
		{
			GameObject newObject = PrefabUtility.InstantiatePrefab(useGameObject) as GameObject;
			Undo.RegisterCreatedObjectUndo(newObject, "created prefab");
			Transform newT = newObject.transform;

			newT.position = t.position;
			newT.rotation = t.rotation;
			newT.localScale = t.localScale;
			newT.parent = t.parent;
		}

		foreach (GameObject go in Selection.gameObjects)
		{
			Undo.DestroyObjectImmediate(go);
		}
	}
}
3 Likes

I added :

newObject.name = go.name;

as well as I needed the names to re-attach them to parameters in one of my scripts needed to know what was what.

If anyone knows of a way to add the references to the new prefab’s please let us know.

Thank you so much guys! This script is a life saver!
Here, I also added a second class which replaces objects based on their name instead of based on selection. (I have thousands of instances of the same prefab, all with broken prefab-connection - but they all have the same name, so in a situation like this, this script is more handy)

public class BatchReplaceByName : ScriptableWizard
{
	public GameObject NewType;
	public string Name = "";
	public bool MatchCase = false;
	
	[MenuItem("Custom/Batch Replace By Name")]
	
	static void CreateWizard()
	{
		var replaceGameObjects = ScriptableWizard.DisplayWizard <BatchReplaceByName>("Replace GameObjects", "Replace");
	}
	
	void OnWizardCreate()
	{
		GameObject[] allObjects = GameObject.FindObjectsOfType<GameObject>();
		if (!MatchCase)
			Name = Name.ToUpper ();

		foreach(GameObject go in allObjects)
		{
			if((MatchCase ? go.name : go.name.ToUpper()) != Name)
				continue;

			GameObject newObject = (GameObject)PrefabUtility.InstantiatePrefab(NewType);
			newObject.transform.parent = go.transform.parent;
			newObject.transform.localPosition = go.transform.localPosition;
			newObject.transform.localRotation = go.transform.localRotation;
			newObject.transform.localScale = go.transform.localScale;
			newObject.name	= go.name;
			UnityEngine.Object.DestroyImmediate(go);
		}
	}
}
2 Likes

Don’t forget to put the script in a folder called Editor (doesn’t matter where, just has to be called “Editor”)

1 Like

Hi, I tweaked it in to an Editor Window and now it can handle Scene objects, Prefabs and Objects!
Enjoy and thanks!

using UnityEditor;
using UnityEngine;
 
public class ReplaceSel : EditorWindow
{
	GameObject myObject;
	
	[MenuItem ("Tools/ReplaceSelected %g")]
	public static void ReplaceObjects() {
		EditorWindow.GetWindow(typeof(ReplaceSel));
	}
		
	void OnGUI () {
		GUILayout.Label ("Use Object", EditorStyles.boldLabel);
		myObject = EditorGUILayout.ObjectField(myObject, typeof(GameObject), true) as GameObject;
		if (GUILayout.Button ("Replace Selected")) {
			
			if (myObject != null) {
				foreach (Transform t in Selection.transforms) {
					GameObject o = null;
					o = PrefabUtility.GetPrefabParent(myObject) as GameObject;
					
					if (PrefabUtility.GetPrefabType(myObject).ToString() == "PrefabInstance") {
						o = (GameObject)PrefabUtility.InstantiatePrefab(o);
						PrefabUtility.SetPropertyModifications(o, PrefabUtility.GetPropertyModifications(myObject));
					}
					
					else if (PrefabUtility.GetPrefabType(myObject).ToString() == "Prefab") {
						o = (GameObject)PrefabUtility.InstantiatePrefab(myObject);
					}
					
					else {
						o = Instantiate(myObject) as GameObject;
					}
					
					Undo.RegisterCreatedObjectUndo(o, "created prefab");
					Transform newT = o.transform;
					newT.position = t.position;
					newT.rotation = t.rotation;
					newT.localScale = t.localScale;
					newT.parent = t.parent;
					
					foreach (GameObject go in Selection.gameObjects) {
						Undo.DestroyObjectImmediate(go);
					}
				}
			}
		}
	}
}
1 Like

Nice script, but it has a bug which makes it unusable. This code should be place outside of the foreach:

foreach (GameObject go in Selection.gameObjects) {

    Undo.DestroyObjectImmediate(go);
}

So it becomes this:

using UnityEditor;
using UnityEngine;

public class ReplaceSel : EditorWindow
{
    GameObject myObject;
    [MenuItem ("Tools/ReplaceSelected %g")]

    public static void ReplaceObjects() {

        EditorWindow.GetWindow(typeof(ReplaceSel));
    }        

    void OnGUI () {

        GUILayout.Label ("Use Object", EditorStyles.boldLabel);

        myObject = EditorGUILayout.ObjectField(myObject, typeof(GameObject), true) as GameObject;

        if (GUILayout.Button ("Replace Selected")) {            

            if (myObject != null) {

                foreach (Transform t in Selection.transforms) {

                    GameObject o = null;
                    o = PrefabUtility.GetPrefabParent(myObject) as GameObject;                    

                    if (PrefabUtility.GetPrefabType(myObject).ToString() == "PrefabInstance") {

                        o = (GameObject)PrefabUtility.InstantiatePrefab(o);
                        PrefabUtility.SetPropertyModifications(o, PrefabUtility.GetPropertyModifications(myObject));
                    }                    

                    else if (PrefabUtility.GetPrefabType(myObject).ToString() == "Prefab") {

                        o = (GameObject)PrefabUtility.InstantiatePrefab(myObject);
                    }                    

                    else {

                        o = Instantiate(myObject) as GameObject;
                    }                    

                    Undo.RegisterCreatedObjectUndo(o, "created prefab");

                    Transform newT = o.transform;

					if(t != null){						

						newT.position = t.position;
						newT.rotation = t.rotation;
						newT.localScale = t.localScale;
						newT.parent = t.parent;  
					}
                }

				foreach (GameObject go in Selection.gameObjects) {

					Undo.DestroyObjectImmediate(go);
				}
            }
        }
    }
}

By the way, there is a wiki for this (similar) script here:
http://wiki.unity3d.com/index.php/ReplaceSelection

2 Likes

This is great, I wish I could see this script earlier. Thank you.

Thank you guys so much! I’m loving this awesome script!

Hey, this script saved me a ton of work. I fixed a bug where you are iterating over an array and destroying items, considered bad practice according to Unity Docs - http://docs.unity3d.com/ScriptReference/Object.DestroyImmediate.html

It also just didn’t work all the time. This explains why: http://answers.unity3d.com/questions/529044/why-is-iterating-an-array-and-destroying-objects-c.html

So, using that as a guide, I have this working perfectly:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;

public class BatchReplaceByName : ScriptableWizard
{
        public GameObject     m_newType;
        public string         m_name = "";
        public bool         m_matchCase = true;
   
        [MenuItem("Custom/Batch Replace By Name")]
   
        static void CreateWizard ()
        {
                var replaceGameObjects = ScriptableWizard.DisplayWizard <BatchReplaceByName> ("Replace GameObjects", "Replace");
                replaceGameObjects.m_name = Selection.activeObject.name;                                                        //Prefill the name field with the active object

        }
   
        void OnWizardCreate ()
        {
                GameObject[] allObjects = GameObject.FindObjectsOfType<GameObject> ();

                List<GameObject> myList = new List<GameObject> ();

                foreach (GameObject g in allObjects) {
                        if ((m_matchCase ? g.name : g.name.ToUpper ()) == m_name) {
                                myList.Add (g);
                        }
                }

                if (!m_matchCase)
                        m_name = m_name.ToUpper ();
               
                for (int i = myList.Count-1; i >= 0; i--) {
       
                        GameObject newObject = (GameObject)PrefabUtility.InstantiatePrefab (m_newType);
                        newObject.transform.parent = myList [i].transform.parent;
                        newObject.transform.localPosition = myList [i].transform.localPosition;
                        newObject.transform.localRotation = myList [i].transform.localRotation;
                        newObject.transform.localScale = myList [i].transform.localScale;
                        newObject.name = myList [i].name;
                        UnityEngine.Object.DestroyImmediate (myList [i]);
                        myList.RemoveAt (i);
                }


        }
}
4 Likes

Awesome script.

I work in cad prorams so I place there the “prefabs” . I hope that now I can change these by a prefab and gain performance.

1 Like

Can someone host this on GitHub already?

1 Like

Thanks so much for the script, saved me a lot of time.

Update from me- the API changed slightly for Unity 5 and this version should work with the new update. Anyone on Unity 4 or earlier will need the previous version.

using UnityEngine;
using UnityEditor;
using System.Collections;
// CopyComponents - by Michael L. Croswell for Colorado Game Coders, LLC
// March 2010
//Modified by Kristian Helle Jespersen
//June 2011
//Modified by Connor Cadellin McKee for Excamedia
//April 2015
public class ReplaceGameObjects : ScriptableWizard
{
    public bool copyValues = true;
    public GameObject NewType;
    public GameObject[] OldObjects;
    [MenuItem("Custom/Replace GameObjects")]
    static void CreateWizard()
    {
        var replaceGameObjects = ScriptableWizard.DisplayWizard <ReplaceGameObjects>("Replace GameObjects", "Replace");
        replaceGameObjects.OldObjects = Selection.gameObjects;
    }   
    void OnWizardCreate()
    {
        //Transform[] Replaces;
        //Replaces = Replace.GetComponentsInChildren<Transform>();       
        foreach (GameObject go in OldObjects)
        {
            GameObject newObject;
            newObject = (GameObject)PrefabUtility.InstantiatePrefab(NewType);
            newObject.transform.parent = go.transform.parent;
            newObject.transform.localPosition = go.transform.localPosition;
            newObject.transform.localRotation = go.transform.localRotation;
            newObject.transform.localScale = go.transform.localScale;           
            DestroyImmediate(go);
        }
    }
}
2 Likes

I’ve improved it:

  • Fixed a warning message that was being fired.
  • Added an option to keep the original objects’ name.
  • Improved field names and cleaned the code.
using UnityEngine;
using UnityEditor;
// CopyComponents - by Michael L. Croswell for Colorado Game Coders, LLC
// March 2010
//Modified by Kristian Helle Jespersen
//June 2011
//Modified by Connor Cadellin McKee for Excamedia
//April 2015
//Modified by Fernando Medina (fermmmm)
//April 2015
public class ReplaceGameObjects : ScriptableWizard
{
    public GameObject Prefab;
    public GameObject[] ObjectsToReplace;
    public bool KeepOriginalNames = true;
    [MenuItem("Custom/Replace GameObjects")]
    static void CreateWizard()
    {
        var replaceGameObjects = DisplayWizard<ReplaceGameObjects>("Replace GameObjects", "Replace");
        replaceGameObjects.ObjectsToReplace = Selection.gameObjects;
    }
    void OnWizardCreate()
    {
        foreach (GameObject go in ObjectsToReplace)
        {
            GameObject newObject;
            newObject = (GameObject)PrefabUtility.InstantiatePrefab(Prefab);
            newObject.transform.SetParent(go.transform.parent, true);
            newObject.transform.localPosition = go.transform.localPosition;
            newObject.transform.localRotation = go.transform.localRotation;
            newObject.transform.localScale = go.transform.localScale;
            if (KeepOriginalNames)
                newObject.transform.name = go.transform.name;
            DestroyImmediate(go);
        }
    }
}
6 Likes