Skip to content

Instantly share code, notes, and snippets.

@sabresaurus
Created June 1, 2016 22:40
Show Gist options
  • Save sabresaurus/000bd478227d6719285e4b38ae741b72 to your computer and use it in GitHub Desktop.
Save sabresaurus/000bd478227d6719285e4b38ae741b72 to your computer and use it in GitHub Desktop.
#if UNITY_EDITOR
using UnityEngine;
using UnityEditor;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using Type = System.Type;
public static class DuplicationUtility
{
public static GameObject DuplicateGameObject(GameObject source)
{
GameObject newObject;
PrefabType prefabType = PrefabUtility.GetPrefabType(source);
if(prefabType == PrefabType.PrefabInstance || prefabType == PrefabType.DisconnectedPrefabInstance)
{
newObject = DuplicatePrefabInstance(source);
}
else // Not a prefab instance or missing prefab reference
{
newObject = GameObject.Instantiate(source);
}
newObject.name = GetDuplicateName(source);
newObject.transform.parent = source.transform.parent;
Undo.RegisterCreatedObjectUndo(newObject, "Duplicate Object(s)");
return newObject;
}
private static GameObject DuplicatePrefabInstance(GameObject source)
{
Object sourcePrefab = PrefabUtility.GetPrefabParent(source);
GameObject spawnedDuplicate = PrefabUtility.InstantiatePrefab(sourcePrefab) as GameObject;
// First of all process the root
ProcessPrefabInstanceObject(source, spawnedDuplicate);
// TODO: Handle new and missing child objects
// Finally return the new object
return spawnedDuplicate;
}
private static void ProcessPrefabInstanceObject(GameObject source, GameObject spawnedDuplicate)
{
// Copy property changes to the new prefab
PropertyModification[] modifications = PrefabUtility.GetPropertyModifications(source);
PrefabUtility.SetPropertyModifications(spawnedDuplicate, modifications);
Component[] sourceComponents = source.GetComponents<Component>();
Component[] newComponents = spawnedDuplicate.GetComponents<Component>();
// for (int i = 0; i < sourceComponents.Length; i++)
// {
// Debug.Log("Source Component: " + sourceComponents[i].GetType().Name + " " + PrefabUtility.GetPrefabType(sourceComponents[i]));
// }
// for (int i = 0; i < newComponents.Length; i++)
// {
// Debug.Log("New Component: " + newComponents[i].GetType().Name + " " + PrefabUtility.GetPrefabType(newComponents[i]));
// }
List<Type> uniqueKeys = new List<Type>();
Dictionary<Type, List<Component>> groupedSourcePrefabComponents = new Dictionary<Type, List<Component>>();
Dictionary<Type, List<Component>> groupedNewPrefabComponents = new Dictionary<Type, List<Component>>();
for (int i = 0; i < sourceComponents.Length; i++)
{
if(PrefabUtility.GetPrefabType(sourceComponents[i]) != PrefabType.None)
{
Type type = sourceComponents[i].GetType();
if(!uniqueKeys.Contains(type))
{
uniqueKeys.Add(type);
}
if(groupedSourcePrefabComponents.ContainsKey(type))
{
groupedSourcePrefabComponents[type].Add(sourceComponents[i]);
}
else
{
groupedSourcePrefabComponents[type] = new List<Component>(){ sourceComponents[i] };
}
}
}
for (int i = 0; i < newComponents.Length; i++)
{
if(PrefabUtility.GetPrefabType(newComponents[i]) != PrefabType.None)
{
Type type = newComponents[i].GetType();
if(!uniqueKeys.Contains(type))
{
uniqueKeys.Add(type);
}
if(groupedNewPrefabComponents.ContainsKey(type))
{
groupedNewPrefabComponents[type].Add(newComponents[i]);
}
else
{
groupedNewPrefabComponents[type] = new List<Component>(){ newComponents[i] };
}
}
}
// Remove any components that are missing from the instance as appropriate
for (int i = 0; i < uniqueKeys.Count; i++)
{
Type key = uniqueKeys[i];
int sourceCount = 0;
int newCount = 0;
if(groupedSourcePrefabComponents.ContainsKey(key))
{
sourceCount = groupedSourcePrefabComponents[key].Count;
}
if(groupedNewPrefabComponents.ContainsKey(key))
{
newCount = groupedNewPrefabComponents[key].Count;
}
if(sourceCount < newCount)
{
int numberToRemove = newCount - sourceCount;
// Remove from the end
// TODO: Note this won't necessarily remove the correct components if there are several of the same type
for (int j = 0; j < numberToRemove; j++)
{
GameObject.DestroyImmediate(groupedNewPrefabComponents[key][newCount - j - 1]);
}
}
}
// Copy any components that have been added to the instance but aren't part of the prefab
for (int i = 0; i < sourceComponents.Length; i++)
{
if(PrefabUtility.GetPrefabType(sourceComponents[i]) == PrefabType.None)
{
Component newComponent = spawnedDuplicate.AddComponent(sourceComponents[i].GetType());
EditorUtility.CopySerialized(sourceComponents[i], newComponent);
}
}
}
private static string GetDuplicateName(GameObject sourceObject)
{
string sourceName = sourceObject.name;
Transform transformParent = sourceObject.transform.parent;
int currentValue = 0;
Match match = Regex.Match(sourceName, "\\ ?\\(\\d+\\)$");
if(match.Success)
{
string stringValue = match.Value.TrimStart();
stringValue = stringValue.Substring(1, stringValue.Length-2);
currentValue = int.Parse(stringValue);
sourceName = sourceName.Substring(0, match.Index);
}
for (int i = 0; i < int.MaxValue-1; i++)
{
currentValue++;
string candidateName = sourceName + " (" + currentValue + ")";
if(transformParent.FindChild(candidateName) == null)
{
return candidateName;
}
}
return sourceName;
}
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment