Applying list of overrides and obtaining ObjectOverride owner

Hi there!

I write a thing which needs to filter overrides, and save only those that are under a specific GO (prefab instance) in the scene hierarchy (named ‘root’ in the following code)

if (PrefabUtility.HasPrefabInstanceAnyOverrides(root.gameObject, false)) {
    var list1 = PrefabUtility.GetAddedGameObjects(root.gameObject);
    var list2 = PrefabUtility.GetAddedComponents(root.gameObject);
    var list3 = PrefabUtility.GetRemovedComponents(root.gameObject);
    var list4 = PrefabUtility.GetObjectOverrides(root.gameObject, false);
   
    List<PrefabOverride> overridesList = new List<PrefabOverride>();
   
    list1.ForEach(item => {
        if (item.instanceGameObject.transform.IsChildOf(root))
            overridesList.Add(item);});
    list2.ForEach(item => {
        if (item.instanceComponent.transform.IsChildOf(root))
            overridesList.Add(item);});
    list3.ForEach(item => {
        if (item.containingInstanceGameObject.transform.IsChildOf(root))
            overridesList.Add(item);});
    list4.ForEach(item => {
        System.Type type = item.instanceObject.GetType();
        if (type == typeof(Transform) && ((Transform)item.instanceObject).IsChildOf(root))
            overridesList.Add(item);
    });

    overridesList.ForEach(item => item.Apply(rootPrefabPath));
}

The list can be large, and then it causes a series of saves in a row, which can be time consuming.

  1. Can we have something like
PrefabUtility.ApplyOverrides(string prefabAssetPath, PrefabOverride[] overrides);

that will save them in one pass?

  1. Can ObjectOverride have a reference to corresponding GameObject? It seems to me wrong to do as I did for list4. Or maybe we can have a
List<PrefabOverride> PrefabUtility.GetOverrides(GameObject prefabInstance);

returning combined list?

Check if wrapping your apply methods in StartAssetEditing/StopAssetEditing makes it faster:

It does, thank you!

Ran into an issue when applying overrides from EditorWindow.OnSelectionChange message function, having another script just called AssetDatabase.Refresh(), resulting in somewhat of broken state:

5855977--622138--applyIssue.png

with error:

Assertion failed on expression: ‘gForceReimports->empty()’
UnityEditor.AssetDatabase:Refresh()
twicebetter.TB_ChangesChecker:Update() (at Assets/Code/twicebetter/Editor/TB_ChangesChecker.cs:30)
UnityEditor.EditorApplication:Internal_CallUpdateFunctions()

pointing to line with AssetDatabase.Refresh() call.

Solved this way for now (in EditorWindow):

void Save() {
    if (EditorApplication.isUpdating) {
        deferredSave = true;
        return;
    }
    deferredSave = false;

    // Applying overrides here
}
void Update() {
    if (deferredSave) Save();
}

Another issue is when applying different types of overrides. For example, if old object is moved, resulting in ObjectOverride for its Transform, and another one GameObject is added, resulting in AddedGameObject for it, applying these overrides in one pass turns references to the added one to become null. So had to split applying into four passes

List<PrefabOverride>[] overrides = new List<PrefabOverride>[]{
    addedGameObjectsOverrides,
    addedComponentsOverrides,
    removedComponentsOverrides,
    objectOverrides
};
for (int i = 0; i < overrides.Length; i++) {
    if (overrides[i].Count > 0) {
        AssetDatabase.StartAssetEditing();
        for (int j = 0; j < overrides[i].Count; j++) overrides[i][j].Apply(rootPrefabPath);
        AssetDatabase.StopAssetEditing();
    }
}