PrefabUtility.RevertObjectOverride does not work correctly in For loops.

PrefabUtility.RevertObjectOverride will not work right in a for loop, for all the affected objects that live within the same prefab. If I revert a component on an object (any component, it doesn’t matter), it will also revert all object components from previous loops. That wouldn’t be a problem, except I’m also trying to apply a new value after the reversion. That new value gets applied, as we can see in the Debug.Log, but it then gets wiped away on the next loop, only when PrefabUtility.RevertObjectOverride command is executed.

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


public class PrefabUtilityBug : Editor
{
    //Every object prior to the last will be re-reverted on every subsequent loop
    [MenuItem("Test/RevertAndApply on Selected")]
    private static void RevertAndApply()
    {
        int loop = 0;
        foreach (GameObject go in Selection.gameObjects)
        {
            PrefabUtility.RevertObjectOverride(go.transform, InteractionMode.AutomatedAction);
            go.transform.localScale = new Vector3(2f, 2f, 2f);

            //You'll notice that each loop has reverted all the previous loops.  And it's not as though nothing happened, we can see the object's scale has changed by the end of its loop.
            for (int i = 0; i <Selection.gameObjects.Length; i++)
            {  
                Debug.Log("           Loop " + loop + ": " + "Object " + i + "'s scale = " + Selection.gameObjects[i].transform.localScale);
            }
            loop++;
        }
    }
    //Reset the objects
    [MenuItem("Test/Revert on Selected")]
    private static void RevertOnly()
    {
        foreach (GameObject go in Selection.gameObjects)
        {
            PrefabUtility.RevertObjectOverride(go.transform, InteractionMode.AutomatedAction);
        }
    }

    //Just for fun, the revert here doesn't even target the transform component, but instead the meshfilter.
    //Despite that, our transform of the gameobjects from previous loops all get reverted
    [MenuItem("Test/RevertUnrelated on Selected")]
    private static void RevertUnrelatedAndApply()
    {
        int loop = 0;
        foreach (GameObject go in Selection.gameObjects)
        {
            PrefabUtility.RevertObjectOverride(go.GetComponent<MeshFilter>(), InteractionMode.AutomatedAction);
            go.transform.localScale = new Vector3(2f, 2f, 2f);

            //You'll notice that each loop has reverted all the previous loops.  And it's not as though nothing happened, we can see the object's scale has changed by the end of its loop.
            for (int i = 0; i < Selection.gameObjects.Length; i++)
            {
                Debug.Log("           Loop " + loop + ": " + "Object " + i + "'s scale = " + Selection.gameObjects[i].transform.localScale);
            }
            loop++;
        }
    }
}
  • Create a prefab with 4 GameObjects inside of it.
  • Add a MeshFilter component, and a Renderer Component to each GameObject.
  • Select GameObject (1), GameObject (2), GameObject (3), GameObject (4).
  • In the menu, select “Test > RevertAndApply on Selected”
  • If you look at the Console, you’ll see the output tell us that each object received a scale override on the current loop, but then that override was removed the next loop, even though the object was not being accessed.

The interesting thing here is if we run “RevertUnrelated on Selected” from the “Test” menu, you’ll notice the same behavior. Even though we’re not even attempting to revert the transform, but instead reverting the meshFilter. This still results in the transform being applied, and then reverted on the next loop, only once the PrefabUtility command is run. Prior to that point, it will have retained its override.

In my actual project, I have a very good reason for doing this, which involves overriding the meshfilter with a clone of the mesh, to add vertex colors into the mesh, and I need to be able to revert so I can update the mesh when a new version of the file is imported. There’s no other way to do that then with a ReverObjectOverride.

This only happens when there’s multiple objects within a single prefab. If you were to select multiple prefabs, they would be able to all receive the overrides within a for loop.

I think it’s likely that the process of mucking around with prefabs is causing the editor app to deselect whatever you currently have selected, and that is the same list you’re looping through while mucking around with prefabs. Try adding all the objects in Selection.gameObjects to a list, and then loop through that list, rather than looping through Selection.gameObjects directly.

it’s actually applying the changes to each object in the selection. I can see in the console output that the object’s scale has been set. But it reverts on the next loop for some reason.

Good news, Unity is working on the issue: Unity Issue Tracker - [Prefabs] PrefabUtility.RevertObjectOverride doesn&#39;t work properly when multiple reverted objects within the same prefab

In the mean time, a coworker found a workaround. Store the selection in a list (like StarManta suggested), then put it through one loop that does the reversion, and then a second loop that applies the new value. This will work successfully.
Unfortunately in my specific case, I’ll have to add a lot of extra code to get this to do everything I need. But, it works.

even better news! Apparently I completely failed to realize there was a method to record the changes made to a prefab.
PrefabUtility.RecordPrefabInstancePropertyModifications()

Calling this will cause the changes to each object within a prefab to be retained after each loop.