Search Unity

Modifying many prefabs extremely slow

Discussion in 'Prefabs' started by RakNet, Apr 4, 2021.

  1. RakNet

    RakNet

    Joined:
    Oct 9, 2013
    Posts:
    315
    If multi-select 1000 assets, something like this takes hours. But if I were to instantiate the objects, assign them in the hierarchy, and save them out, it would take like 5 minutes.

    Code (CSharp):
    1. [MenuItem("Assets/Util/Prefragment/AssignMeshFromFilterToCollider", false, 2351)]
    2. private static void AssignMeshFromFilterToCollider()
    3. {
    4.     UnityEngine.Object[] selectedObjects = Selection.GetFiltered(typeof(UnityEngine.GameObject), SelectionMode.Assets);
    5.  
    6.     foreach (GameObject selectedSceneOrPrefabObject in selectedObjects)
    7.     {
    8.         string assetPath = AssetDatabase.GetAssetPath(selectedSceneOrPrefabObject);
    9.         var root = PrefabUtility.LoadPrefabContents(assetPath);
    10.  
    11.         MeshFilter[] mrs = root.GetComponentsInChildren<MeshFilter>();
    12.         foreach (MeshFilter mr in mrs)
    13.         {
    14.             MeshCollider mc = mr.gameObject.GetComponent<MeshCollider>();
    15.             if (mc != null)
    16.                 mc.sharedMesh = mr.sharedMesh;
    17.         }
    18.  
    19.         PrefabUtility.SaveAsPrefabAsset(root, assetPath);
    20.         PrefabUtility.UnloadPrefabContents(root);
    21.  
    22.     }
    23. }
    Is there a way to speed this up? Maybe AssetDatabase.StartAssetEditing?
     
  2. RakNet

    RakNet

    Joined:
    Oct 9, 2013
    Posts:
    315
    This is absurd and really in need of optimization or better documentation. I waited for an hour, gave up, and forced shut down the editor.

    I wrote a script in 5 minutes to do the same operation in the Hierarchy, and it executed across all 1000 objects in 50 seconds.
     
  3. codestage

    codestage

    Joined:
    Jul 27, 2012
    Posts:
    1,931
    Just in case anyone will came across this: you can iterate prefabs much faster without actually opening them into the scene \ stage if you don't need any specific initialization stuff to execute:

    Code (CSharp):
    1. public static GameObject GetPrefabAssetRoot(string path)
    2. {
    3.    return AssetDatabase.LoadMainAssetAtPath(path) as GameObject;
    4. }
    Regardless, I totally agree PrefabUtility.LoadPrefabContents performance is a nonsense.
     
  4. codestage

    codestage

    Joined:
    Jul 27, 2012
    Posts:
    1,931
    Just checked out and wow, seeing a huge improvement from ~25000 ms at Unity 2019 LTS down just ~600-700 ms at Unity 2020 LTS on 537 prefabs iterating.

    Also, to make most out of PrefabUtility.LoadPrefabContents if you still need it instead of AssetDatabase.LoadMainAssetAtPath, consider reusing preview scene (roots list reusing is not necessary but would be a bonus to reduce GC which is no one cares in Edit mode anyways):

    Code (CSharp):
    1. var scene = EditorSceneManager.NewPreviewScene();
    2. var roots = new List<GameObject>();
    3.  
    4. foreach (var prefab in allPrefabsPaths)
    5. {
    6.     PrefabUtility.LoadPrefabContentsIntoPreviewScene(prefab, scene);
    7.     scene.GetRootGameObjects(roots);
    8.     var root = roots[0];
    9.     IterateAllGameObjects(root, true);
    10. }
    11.  
    12. EditorSceneManager.ClosePreviewScene(scene);
     
  5. SomeVVhIteGuy

    SomeVVhIteGuy

    Joined:
    Mar 31, 2018
    Posts:
    162

    Would this work for manipulating variables of two gameobjects that have the same base prefab?
     
  6. codestage

    codestage

    Joined:
    Jul 27, 2012
    Posts:
    1,931
    Yes, just look for your game objects and change those properties, it should work just fine.
     
    SomeVVhIteGuy likes this.