- Home /
Reverting several GameObjects to Prefab settings at once
Hey all,
I've ran into an issue this evening. I have a Prefab that contains a simple cube mesh and a material, and I've placed hundreds of instances of this through several different scenes.
I was attempting to change the prefab material when I realized that all the instances have overridden the material property due to something I must have changed on the first instance I ever created, thus not allowing me to reflect the changes to the Prefab across all of its instances.
I know that you can revert an instance to its Prefab but even when selecting all the instances (each scene they are all contained inside an empty GameObject) the revert only affects the last one I selected.
Is there no way to execute variable changes across multiple selected GameObjects? If not I assume I need to write an Editor script, something I have not delved into yet but if it comes to that I'm sure the experience will be worth something.
Hope I'm not overlooking something ridiculous here, it's 5:30am so I'm prepared for it. I've searched thoroughly and the only thing I found to go on is this thread on the main forums. It's dated 2009 though so I was hoping things have changed since then.
Thanks in advance!
Answer by AdamOwen · Jul 16, 2011 at 05:27 PM
Well after more unsuccessful searching I read up on the Editor scripting reference and managed to write a script that resets all selected GameObjects to their prefab settings.
@MenuItem ("Tools/Revert to Prefab %r")
static function Revert() {
var selection = Selection.gameObjects;
if (selection.length > 0) {
for (var i : int = 0; i < selection.length; i++) {
EditorUtility.ResetGameObjectToPrefabState(selection[i]);
}
} else {
Debug.Log("Cannot revert to prefab - nothing selected");
}
}
As I said I'm new to scripting the editor and it may not be the best way to do this but it works for me and that's all I need right now.
Hate to bump up an old question but is there really no way to do this within stock Unity?
Thanks!
Slight update: nowadays you'd have to call PrefabUtility.RevertPrefabInstance(GameObject), as EditorUtility.ResetGameObjectToPrefabState(GameObject) doesn't exist anymore.
@Udev$$anonymous$$ike Unfortunately, Unity doesn't support it by default. They expose the PrefabUtility class so that you can spin up your own scripts but its a bit fiddly. The function you want can be found in the Unity Docs.
Alternatively, you can use a premade asset such as $$anonymous$$ultifab that does all the hard bits for you.
Answer by jim_altvr · Feb 23, 2016 at 01:12 PM
Non-programmer here... Had a coworker update your script to work in Unity 5(.2.3)
using UnityEngine;
using UnityEditor;
using System.Collections;
public class RevertPrefabInstance : MonoBehaviour {
[MenuItem ("Tools/Revert to Prefab %r")]
static void Revert() {
var selection = Selection.gameObjects;
if (selection.Length > 0) {
for (var i = 0; i < selection.Length; i++) {
PrefabUtility.RevertPrefabInstance(selection[i]);
}
} else {
Debug.Log("Cannot revert to prefab - nothing selected");
}
}
}
Thanks you too for posting an updated version! Saved me a lot of time!
Answer by x4000 · Apr 28, 2016 at 01:54 PM
Here's a much more robust version:
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
/// <summary>
/// http://answers.unity3d.com/questions/144453/reverting-several-gameobjects-to-prefab-settings-a.html
/// </summary>
public class RevertPrefabInstance : UnityEditor.Editor
{
[MenuItem( "Tools/Revert Selected Prefabs %&#r" )]
static void Revert()
{
GameObject[] selection = Selection.gameObjects;
if ( selection.Length > 0 )
{
int revertedCount = 0;
Dictionary<UnityEngine.Object,bool> prefabsAlreadyReverted = new Dictionary<UnityEngine.Object, bool>();
//we tell the user that we're about to do an expensive operation, because otherwise they may think the editor is hanging
//additionally if the hotkey doesn't work (aka clash with another hotkey) they won't even know it failed to run!
EditorUtility.DisplayDialog( "Please wait", "Checking " + selection.Length + " objects and their children to revert prefab status -- this may take a while!", "OK" );
for ( int i = 0; i < selection.Length; i++ )
RecursiveRevertPrefabInstances( selection[i], ref revertedCount, ref prefabsAlreadyReverted );
//tell them we finished, because otherwise they have no easy way to notice if it takes a bit!
EditorUtility.DisplayDialog( "All done!", "Performed " + revertedCount + " reversions.", "OK" );
}
else {
EditorUtility.DisplayDialog( "Error", "Cannot revert to prefab - nothing selected.", "OK" );
}
}
/// <summary>
/// This allows for both nested prefabs as well as simply going into object trees without having to expand the whole tree first.
/// </summary>
static void RecursiveRevertPrefabInstances( GameObject obj, ref int revertedCount, ref Dictionary<UnityEngine.Object, bool> prefabsAlreadyReverted )
{
if ( obj == null )
return;
if ( IsAPrefabNotYetReverted( obj, ref prefabsAlreadyReverted ) )
{
revertedCount++;
PrefabUtility.RevertPrefabInstance( obj );
}
Transform trans = obj.transform;
for ( int i = 0; i < trans.childCount; i++ )
RecursiveRevertPrefabInstances( trans.GetChild( i ).gameObject, ref revertedCount, ref prefabsAlreadyReverted );
}
/// <summary>
/// This keeps us from reverting the same prefab over and over, which otherwise happens when we're doing checks for nested prefabs.
/// </summary>
static bool IsAPrefabNotYetReverted( GameObject obj, ref Dictionary<UnityEngine.Object, bool> prefabsAlreadyReverted )
{
bool wasValidAtEitherLevel = false;
UnityEngine.Object prefab = PrefabUtility.GetPrefabParent( obj );
if ( prefab != null && !prefabsAlreadyReverted.ContainsKey( prefab ) )
{
wasValidAtEitherLevel = true;
prefabsAlreadyReverted[prefab] = true;
}
prefab = PrefabUtility.GetPrefabObject( obj );
if ( prefab != null && !prefabsAlreadyReverted.ContainsKey( prefab ) )
{
wasValidAtEitherLevel = true;
prefabsAlreadyReverted[prefab] = true;
}
return wasValidAtEitherLevel;
}
}