As you probably know, when duplicating a game object, duplicated objects goes to bottom of hierarchy.
Which is so counter intuitive and counter productive.
Here is a small replacement of Ctrl+D, (with a custom Shift+D) that does exactly the same but in addition place the duplicated objects below it’s source, in hierarchy.
Copy and paste this code in a new file in your project :
public static class EditorDuplicateCommand
{
[MenuItem ( "Edit/Duplicate #d", false, 0 )]
static void DuplicateSelection ()
{
List<GameObject> newSelection = new List<GameObject> ();
for ( int ui = 0; ui < Selection.gameObjects.Length; ui++ )
{
GameObject go = Selection.gameObjects[ui];
int siblingIndex = go.transform.GetSiblingIndex ();
GameObject newGo = GameObject.Instantiate ( go );
newGo.transform.parent = go.transform.parent;
newGo.transform.position = go.transform.position;
newGo.transform.SetSiblingIndex ( siblingIndex + 1 );
Undo.RegisterCreatedObjectUndo ( newGo, "Duplicate" );
newSelection.Add ( newGo );
}
Selection.objects = newSelection.ToArray ();
}
}
This would be so amazing. Has boggled my mind for years how counter productive it is currently. I tried the script but I get a bunch of errors? For example, can’t find MenuItem.
This is a almost a solution to a very annoying problem in Unity. The only issue I have with it is that it breaks instances of prefabs and turns them into singular game objects.
Can you make it so it duplicates instances without breaking the prefab connection.
I modified the code so that it no longer breaks prefab connections.
It will now properly duplicate objects, prefab instances, and prefabs.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public static class EditorDuplicateCommand {
// Using &d (alt+d) instead of #d (ctrl+d) because typing capital D was triggering this
[MenuItem("Edit/Duplicate &d", false, 0)]
static void DuplicateSelection () {
var newSelection = new List<GameObject>();
for (int ui = 0; ui < Selection.gameObjects.Length; ui++) {
var go = Selection.gameObjects[ui];
int siblingIndex = go.transform.GetSiblingIndex();
var newGo = SmartInstantiate(go);
newGo.transform.parent = go.transform.parent;
newGo.transform.position = go.transform.position;
newGo.transform.SetSiblingIndex(siblingIndex + 1);
Undo.RegisterCreatedObjectUndo(newGo, "Duplicate");
newGo.name = go.name;
newSelection.Add(newGo);
}
Selection.objects = newSelection.ToArray();
}
private static GameObject SmartInstantiate(GameObject go) {
var prefabType = PrefabUtility.GetPrefabType(go);
if (prefabType == PrefabType.PrefabInstance) {
var prefab = PrefabUtility.GetPrefabParent(go) as GameObject;
var newGo = (GameObject) PrefabUtility.InstantiatePrefab(prefab);
var mods = PrefabUtility.GetPropertyModifications(go);
PrefabUtility.SetPropertyModifications(newGo, mods);
return newGo;
}
if (prefabType == PrefabType.Prefab) {
return (GameObject)PrefabUtility.InstantiatePrefab(go);
}
return GameObject.Instantiate(go) as GameObject;
}
}
Oh man, please fix this, I’m currently working on a project that has a lot of objects, like a lot, and I need to duplicate some and keep them below the original but they get added at the end. It is very, very annoying!
Hey, this is really good and saved my life
I rewrite legacy functions and make some optimization on Asset Duplicate so it duplicated with increasing ID
Note: I use Unity Shortcut Manager to solve conflict
And I write a tutorial in my blog, hope it helps
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public static class EditorDuplicateCommand
{
[MenuItem("Edit/Smart Duplicate %d", false, 0)]
private static void DuplicateSelection()
{
var newSelection = new List<Object>();
if (Selection.gameObjects.Length > 0)
{
for (int ui = 0; ui < Selection.gameObjects.Length; ui++)
{
var go = Selection.gameObjects[ui];
int siblingIndex = go.transform.GetSiblingIndex();
var newGo = SmartInstantiate(go);
newGo.transform.parent = go.transform.parent;
newGo.transform.position = go.transform.position;
newGo.transform.SetSiblingIndex(siblingIndex + 1);
newGo.name = go.name;
newSelection.Add(newGo);
}
}
else
{
for (int ui = 0; ui < Selection.objects.Length; ui++)
{
var go = Selection.objects[ui];
var newGo = DuplicateAsset(go);
newSelection.Add(newGo);
}
}
Selection.objects = newSelection.ToArray();
}
private static GameObject SmartInstantiate(GameObject go)
{
if (PrefabUtility.GetPrefabInstanceStatus(go) == PrefabInstanceStatus.Connected)
{
var prefab = PrefabUtility.GetCorrespondingObjectFromSource(go) as GameObject;
var newGo = (GameObject)PrefabUtility.InstantiatePrefab(prefab);
var mods = PrefabUtility.GetPropertyModifications(go);
PrefabUtility.SetPropertyModifications(newGo, mods);
return newGo;
}
if (PrefabUtility.GetPrefabAssetType(go) != PrefabAssetType.NotAPrefab)
{
return DuplicateAsset(go) as GameObject;
}
return GameObject.Instantiate(go) as GameObject;
}
private static Object DuplicateAsset(Object targetAsset)
{
int index = 1;
while (AssetDatabase.LoadAssetAtPath<Object>(AssetDatabase.GetAssetPath(targetAsset).Replace(".", " " + index + ".")) != null)
{
index++;
if (index > 100)
{
Debug.LogError("Massive Asset Duplicate Detect");
}
}
AssetDatabase.CopyAsset(AssetDatabase.GetAssetPath(targetAsset), AssetDatabase.GetAssetPath(targetAsset).Replace(".", " " + index + "."));
return AssetDatabase.LoadAssetAtPath<Object>(AssetDatabase.GetAssetPath(targetAsset).Replace(".", " " + index + "."));
}
}