x


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!

more ▼

asked Mar 05 '10 at 10:41 PM

mcroswell gravatar image

mcroswell
245 4 4 13

Can you please post your full working script?

Sep 16 '11 at 08:11 PM J3 Gaming
(comments are locked)
10|3000 characters needed characters left

2 answers: sort voted first

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));
}
more ▼

answered Mar 07 '10 at 10:08 PM

SimTex gravatar image

SimTex
191 1 2 7

This works for about 90% of unity objects, but there's some weird cases that'll bite you (like ParticleEmitters).

Mar 11 '10 at 06:33 PM Mortoc

Thank you! I added

using System.Reflection;

and modified your code above to fit my current variables.

Works great!!

Mar 23 '10 at 03:00 AM mcroswell

Motorc: 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.

Mar 23 '10 at 05:36 AM mcroswell

While I definitely wish you the best, I hope even more that unity gets this basic feature out-of-the-box eventually.

Jun 19 '10 at 09:15 PM Waz

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.

Jan 09 '11 at 07:11 AM yoyo
(comments are locked)
10|3000 characters needed characters left

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));
more ▼

answered Sep 20 '11 at 03:44 AM

DR9885 gravatar image

DR9885
16

You wrote NonPublic twice. I think you meant "BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic"

Feb 08 '12 at 08:55 AM piojo
(comments are locked)
10|3000 characters needed characters left

There's also a general solution for this problem: http://unitydevs.com/item/copypaste-component/

more ▼

answered Aug 26 '10 at 10:11 PM

Ranza gravatar image

Ranza
192 14 15 27

@Ranza: Useful. But again, notice it has some serious limitations: no serializable components, no support for Light, ParticleEmitters (probably others). By the way, I was contacted by another scripter/programmer who is working on his own version. Still, mine had bugs and I am hoping (as Warwick Allison above) that it will be an "out-of-the-box" feature soon! For what it's worth: If you use other's scripts, particularly when copying/pasting components I highly recommend you backup your scenes very frequently (not a bad idea when using Unity anyhow).

Sep 07 '10 at 10:42 PM mcroswell
(comments are locked)
10|3000 characters needed characters left
Your answer
toggle preview:

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this question

By Email:

Once you sign in you will be able to subscribe for any updates here

By RSS:

Answers

Answers and Comments

Topics:

x3325
x1670
x303
x130
x14

asked: Mar 05 '10 at 10:41 PM

Seen: 7605 times

Last Updated: Feb 08 '12 at 08:55 AM