Created
June 1, 2016 22:40
-
-
Save sabresaurus/000bd478227d6719285e4b38ae741b72 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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