- Home /
Editor Wizard: Copy Existing Components to Another GameObject
Since the editor does not have a way to copy the child components of a game object and apply that copy to another game object, I am trying to make a Editor Wizard that will do this.
My wizard is able to copy the components presently, but cannot copy the values of the source.
Any ideas?
Here's what I have so far (below). The commented-out code doesn't work (so, for example, I know that CloneComponent will just make clones in the source - makes a mess so don't use - it will duplicate your transform components, for example - not good!). I left the comments in so folks realize that I've tried the obvious code from the reference and so we don't "reinvent the broken wheel" so to speak.
using UnityEngine;
using UnityEditor;
using System.Collections;
// CopyComponents - by Michael L. Croswell for Colorado Game Coders, LLC
// March 2010
public class CopyComponents : ScriptableWizard
{
public bool copyValues = true;
public GameObject fromObject;
public GameObject toObject;
[MenuItem ("Custom/Copy Components")]
static void CreateWizard ()
{
ScriptableWizard.DisplayWizard("Copy Components", typeof(CopyComponents), "Copy");
}
void OnWizardCreate ()
{
//foreach (GameObject go in fromObject.GetFiltered(typeof(GameObject), SelectionMode.TopLevel))
Component[] fromComps = fromObject.GetComponents(typeof(Component));
Component[] toComps = toObject.GetComponents(typeof(Component));
string msg = "";
msg += "FromObject total components count is " + fromComps.Length;
msg += "\nToObject total components count is " + toComps.Length;
/// Blows up Unity iPhone: EditorUtility.CopySerialized(fromObject, toObject); // Source to Destination
/// return;
int i = 0;
for (i=0; i < fromComps.Length; i++)
{
string t1 = fromComps[i].GetType().Name;
string t2 = "";
msg += "\n#" + i + " is type " + t1;
if (i < toComps.Length)
t2 = toComps[i].GetType().Name;
if (t2 != t1)
{
//if (copyValues)
// toObject.AddComponent(EditorUtility.CloneComponent(fromComps[i]));
//else
toObject.AddComponent(fromComps[i].GetType());
toComps = toObject.GetComponents(typeof(Component));
if (i < toComps.Length)
{
Debug.Log("toComps.Length is " + toComps.Length + " i is " + i);
// Not good at all: EditorUtility.CopySerialized(fromComps[i], toComps[i]); // Source to Destination
toComps[i] = fromComps[i]; // Does nothing apparently.
// This doesn't work it just duplicates inside fromObject: EditorUtility.CloneComponent(fromComps[i]);
msg += "\t Add To: Increasing toObject component count to " + toComps.Length;
}
else
{
msg += "\t Problem: i is " + i + " but toComps is only " + toComps.Length;
}
}
else
{
if (copyValues)
{
// Doesn't make a difference: Destroy(toComps[i]);
//Duplicates in Source (not good either): toObject.AddComponent(EditorUtility.CloneComponent(fromComps[i]));
// Not good: EditorUtility.CopySerialized(fromComps[i], toComps[i]); // Source to Destination
toComps[i] = fromComps[i]; //Does nothing.
// Trouble here also: EditorUtility.CloneComponent(fromComps[i]);
msg += "\t Cloned values.";
}
}
}
EditorUtility.DisplayDialog("Game Object Facts", msg , "OK", "");
}
}
Thanks in advance!
Answer by SimTex · Mar 07, 2010 at 10:08 PM
Use reflection.
Component new_component = gameObject.AddComponent(old_component.GetType());
foreach (FieldInfo f in old_component.GetType().GetFields())
{
f.SetValue(new_component, f.GetValue(old_component));
}
This works for about 90% of unity objects, but there's some weird cases that'll bite you (like ParticleEmitters).
Thank you! I added
using System.Reflection;
and modified your code above to fit my current variables.
Works great!!
$$anonymous$$otorc: Thanks for the heads=up. I will probably try to put in special-case code for ParticleEmitters. Currently my project doesn't use them, but I am planning on making this general usage and sharing.
While I definitely wish you the best, I hope even more that unity gets this basic feature out-of-the-box eventually.
I suspect the "weird cases" actually have C# attribute markup on the fields (and properties?) that you need to consider. Have a look at FieldInfo.GetCustomAttributes (and PropertyInfo.GetCustomAttributes) and see how the Unity devs have marked up those weird cases.
Answer by Eran-Yaacobi · Dec 28, 2014 at 11:26 PM
A better approach would be to use UnityEditorInternal namespace:
UnityEditorInternal.ComponentUtility.CopyComponent(componentToCopy);
UnityEditorInternal.ComponentUtility.PasteComponentAsNew(targetGameObject);
You can also use PasteComponentValues instead of PasteComponentAsNew.
Just want to note that those methods are "relatively new". They did not exist back then. They got added when the editor got the copy and paste functions inside the context menu. So now you can simply right click on a component header line and copy / paste a component.
This the most important thing in this page.... thx so much.
Answer by DR9885 · Sep 20, 2011 at 03:44 AM
To add to that Reflection suggestion, if you are using private serialized variables, like me, you may want something more like this...
foreach (FieldInfo info in comp.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.NonPublic))
if(info.IsPublic || info.GetCustomAttributes(typeof(SerializeField),true).Length != 0);
info.SetValue(newComponent, info.GetValue(comp));
You wrote NonPublic twice. I think you meant "BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic"
Using this method, I was able to successfully copy components that I'd written in my project, however I am unable to copy Unity built-in components such as BoxCollider - any ideas on how we can improve the function in order to do this?
Your answer
Follow this Question
Related Questions
Is there a way to detect "On Component Attach" and "On Component Remove"?? 2 Answers
How to make changes to a copied object 1 Answer
Script component in Editor vs. runtime-dependent inheritance 0 Answers
how to disable a script component 1 Answer
How to undo removing components in an editor extension? 0 Answers