Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice
  3. Dismiss Notice

[SOLVED] Duplicate Prefab Issue

Discussion in 'Prefabs' started by CaseyHofland, Nov 16, 2019.

  1. CaseyHofland

    CaseyHofland

    Joined:
    Mar 18, 2016
    Posts:
    621
    Hi there, I'm working on a lovely little duplicator tool.

    duplicator.gif

    The main idea is that it can be used together with Snaps (Asset Store) to create prototyping environments even faster!

    Unfortunately, I still have one issue with prefabs I can't seem to resolve.

    duplicateprefab.gif

    What happens is that I am instantiating the Prefab via PrefabUtility.Instantiate, which doesn't copy any overrides (under which the Transform override).

    prefab difference.jpg

    This won't do. When I use duplicate (Ctrl + d) on a prefab, all of the instances overrides are carried over as you'd expect. This is what I need! Is there any way to access that behaviour via code? I've already scoured the docs on PrefabUtility, but the documentation there is lacking a bit, and the documentation on UnityEditorInternal is non-existent. I'm kind-of out my debt on this, I'm not someone to post needlessly.

    For good measure, here is the code I use to instantiate the new objects (the translation equals the offset from the original source).

    Code (CSharp):
    1. void InstantiateObjects(Vector3 translation)
    2.             {
    3.                 foreach(var gameObject in TargetQuery)
    4.                 {
    5.                     GameObject go = PrefabUtility.GetCorrespondingObjectFromSource(gameObject);
    6.  
    7.                     if(go)
    8.                     {
    9.                         go = PrefabUtility.InstantiatePrefab(go, gameObject.transform.parent) as GameObject;
    10.                     }
    11.                     else
    12.                     {
    13.                         go = GameObject.Instantiate(gameObject, gameObject.transform.parent);
    14.                         go.name = gameObject.name;
    15.                     }
    16.  
    17.                     go.transform.Translate(translation, Space.World);
    18.  
    19.                     Undo.RegisterCreatedObjectUndo(go, "Created Object");
    20.                 }
    21.             }
     
    Last edited: Nov 16, 2019
  2. CaseyHofland

    CaseyHofland

    Joined:
    Mar 18, 2016
    Posts:
    621
    Quick update, I have now solved part of this issue doing this:

    Code (CSharp):
    1. ...
    2. GameObject go = PrefabUtility.GetCorrespondingObjectFromSource(gameObject);
    3.  
    4. if(go)
    5. {
    6.     go = PrefabUtility.InstantiatePrefab(go, gameObject.transform.parent) as GameObject;
    7.     PrefabUtility.SetPropertyModifications(go, PrefabUtility.GetPropertyModifications(gameObject));
    8. }
    9. ...
    This will copy all the property modifications (like transform.position & MeshRenderer.MotionVectors) but NOT added and removed components (like Mesh Collider and Thing) in the above example.

    Unfortunately, this is the only Set() method that PrefabUtility has, and GetObjectOverrides() doesn't include added and removed components.

    I will use GetAdded- and GetRemovedComponents() and loop through these, but I still don't believe you don't support copying a prefab as an instance. I believe that is something Unity should manage.
     
    ModLunar likes this.
  3. CaseyHofland

    CaseyHofland

    Joined:
    Mar 18, 2016
    Posts:
    621
    So after hitting up the forums once again and digging a bit deeper, I finally found this:

    Code (CSharp):
    1. SceneView.lastActiveSceneView.Focus();
    2. EditorWindow.focusedWindow.SendEvent(EditorGUIUtility.CommandEvent("Duplicate"));
    Which FINALLY lets me call Duplicate from script, causing my prefabs to stay intact (at least as long as Unity doesn't break Duplicate).
     
  4. ModLunar

    ModLunar

    Joined:
    Oct 16, 2016
    Posts:
    374
    It'd be nice to have a less anxiety-inducing way to duplicate objects in my editor scripts, but this will work for now haha.

    Thanks @CaseyHofland ! :)
     
    Last edited: Dec 5, 2020
    Bastienre4 and CaseyHofland like this.
  5. ModLunar

    ModLunar

    Joined:
    Oct 16, 2016
    Posts:
    374
    For anyone looking for a code snippet, this is roughly what I used to duplicate a prefab instance in the Unity editor, preserving not only PropertyModifications, but also added GameObject(s) and Component(s).

    I had to send the "Duplicate" event to the Hierarchy View (where you see your Transform hierarchies in the editor).
    The Hierarchy View is where I usually go select objects, and press Ctrl + D to duplicate them.
    The code below does exactly this:

    Code (CSharp):
    1. using System;
    2. using UnityEditor;
    3. using UnityEngine;
    4.  
    5. using Object = UnityEngine.Object;
    6.  
    7. public static class EditorWindowUtil {
    8.     //This method finds the first EditorWindow that's open, and is of the given type.
    9.     //For example, this is how we can search for the "SceneHierarchyWindow" that's currently open (hopefully it *is* actually open).
    10.     public static EditorWindow FindFirst(Type editorWindowType) {
    11.         if (editorWindowType == null)
    12.             throw new ArgumentNullException(nameof(editorWindowType));
    13.         if (!typeof(EditorWindow).IsAssignableFrom(editorWindowType))
    14.             throw new ArgumentException("The given type (" + editorWindowType.Name + ") does not inherit from " + nameof(EditorWindow) + ".");
    15.  
    16.         Object[] openWindowsOfType = Resources.FindObjectsOfTypeAll(editorWindowType);
    17.         if (openWindowsOfType.Length <= 0)
    18.             return null;
    19.  
    20.         EditorWindow window = (EditorWindow) openWindowsOfType[0];
    21.         return window;
    22.     }
    23.  
    24.     //Works with prefab modifications, AND added GameObjects/Components!
    25.     //The PrefabUtility API does not have a method that does this as of Unity 2020.1.3f1 (August 24, 2020). For shame.
    26.     public static GameObject DuplicatePrefabInstance(GameObject prefabInstance) {
    27.         Object[] previousSelection = Selection.objects;
    28.         Selection.objects = new Object[] { prefabInstance };
    29.         Selection.activeGameObject = prefabInstance;
    30.  
    31.         //For performance, you might want to cache this Reflection:
    32.         Type hierarchyViewType = Type.GetType("UnityEditor.SceneHierarchyWindow, UnityEditor");
    33.         EditorWindow hierarchyView = EditorWindowUtil.FindFirst(hierarchyViewType);
    34.  
    35.         //Using the Unity Hierarchy View window, we can duplicate our selected objects!
    36.         hierarchyView.SendEvent(EditorGUIUtility.CommandEvent("Duplicate"));
    37.  
    38.         GameObject clone = Selection.activeGameObject;
    39.         Selection.objects = previousSelection;
    40.         return clone;
    41.     }
    42. }
    43.  
     
    Last edited: Dec 6, 2020
  6. LazloBonin

    LazloBonin

    Joined:
    Mar 6, 2015
    Posts:
    822
    Thanks for the tip, really helpful, albeit a bit ridiculous that we can't just use a built-in API like EditorUtility.Duplicate instead of this hack.
     
    Last edited: Oct 9, 2021
    Darkgaze, ModLunar and Bastienre4 like this.
  7. Bastienre4

    Bastienre4

    Joined:
    Jul 8, 2014
    Posts:
    191
    +1, I would also like a clean way of doing this. :)
     
    ModLunar likes this.
  8. LazloBonin

    LazloBonin

    Joined:
    Mar 6, 2015
    Posts:
    822
    So I did find a cleaner solution in the Unsupported class! It's actually very easy:

    Code (CSharp):
    1. var previousSelection = Selection.objects;
    2. Selection.activeGameObject = gameObject;
    3. Unsupported.DuplicateGameObjectsUsingPasteboard();
    4. duplicate = Selection.activeGameObject;
    5. Selection.objects = previousSelection;
     
  9. ModLunar

    ModLunar

    Joined:
    Oct 16, 2016
    Posts:
    374
    Hahah whatt!!
    "Unsupported"??

    Err.. I mean I'll take it, but the class name makes me nervous XD.
    Nice find Ludiq!
     
    Darkgaze and Bastienre4 like this.
  10. yodda

    yodda

    Joined:
    Nov 3, 2016
    Posts:
    23
    now it's addressable objects.
    unity needs to have an easy way for duplicating, saving, loading, move on grid prefabs in the stage and editor.
    I need to use addressable that forces me to go online.
    if I want stand-alone I need to use the JSON utility for saving but not vector3 or quaternions
    I need to create a class for it and then break apart the x y z into an array.
    why ?
    because simple JSON that supported JsonArray is gone.
    I mean ... common ... unity expects from people that want to create to go through havoc.
    unity needs to provide simple scripts that will function like components.
    they need to have a pool of them and we just need to drag and drop in the inspector.
    for example, save load, grid, color picking, dragging, etc... and all the expensive and elementary assets that people buy from the unity store.