How to get a component from an object and add it to another? (Copy components at runtime)

EDIT: a much more solid way is to serialize/deserialize the component (clone it) - See FastSave in Vfw (Free)


Hello, I have a “FILE” and “TextFile”, “ImageFile”, “AudioFile” and “VideoFile” as children to (inherit from) “FILE”.

“FILE” is abstract so I don’t add it to my objects, I add the other scripts, text, image, etc. - What I wanna do, is once I pickup a text/image/video/audio file, I wanna ‘get’ the text/image/video/audio file script/component and add it to another object I have elsewhere.

I tried something like this:

var fileScript = fileObj.GetComponent(typeof(FILE));

I think this gets it fine, right?
I mean, if fileObj had a TextFile attached, this should get me that component, because a TextFile is of type FILE (inherits from it - TextFile ‘is a’ FILE)… RIGHT?

But then how to add it to my other object? tried something like this:

myOtherObj.AddComponent(fileScript.ToString());

but my guess is, is that the previous adds a new component, not the same one I picked/got from my fileObj, I wanna add the SAME one, how?

This also doesn’t work:

myOtherObj.AddComponent(fileScript);

Some comments and clarifications if you can on the return types of GetComponent and the inputs of AddComponent are very much appreciated as well.

Thanks a lot.

So I finally found a solution to copying components at runtime - via reflection. Thanks to @Jamora who pointed out that I could set values via reflection too, not just get them.

This extension method gets a copy of a component:

public static T GetCopyOf<T>(this Component comp, T other) where T : Component
{
	Type type = comp.GetType();
	if (type != other.GetType()) return null; // type mis-match
	BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default | BindingFlags.DeclaredOnly;
	PropertyInfo[] pinfos = type.GetProperties(flags);
	foreach (var pinfo in pinfos) {
		if (pinfo.CanWrite) {
			try {
				pinfo.SetValue(comp, pinfo.GetValue(other, null), null);
			}
			catch { } // In case of NotImplementedException being thrown. For some reason specifying that exception didn't seem to catch it, so I didn't catch anything specific.
		}
	}
	FieldInfo[] finfos = type.GetFields(flags);
	foreach (var finfo in finfos) {
		finfo.SetValue(comp, finfo.GetValue(other));
	}
	return comp as T;
}

Usage:

var copy = myComp.GetCopyOf(someOtherComponent);

To make things more convenient, I added this GameObject extension method to add a component directly by just passing it so a copy of it will get added:

public static T AddComponent<T>(this GameObject go, T toAdd) where T : Component
{
	return go.AddComponent<T>().GetCopyOf(toAdd) as T;
}

Usage:

Health myHealth = gameObject.AddComponent<Health>(enemy.health);

Have fun :smiley:

EDIT:

DON’T USE EDITOR CODE IN YOUR SHIPPABLE CODE - IT WON’T BUILD.


Thanks to @Statement for his comment here, It is possible to copy/paste components values, at runtime - using the same functionality used by the editor.

The functionality is hidden, and undocumented sitting inside UnityEditorInternal.ComponentUtility

Here’s a peek inside:

	[WrapperlessIcall]
	public static bool CopyComponent(Component component);
	[WrapperlessIcall]
	public static bool MoveComponentDown(Component component);
	[WrapperlessIcall]
	public static bool MoveComponentUp(Component component);
	[WrapperlessIcall]
	public static bool PasteComponentAsNew(GameObject go);
	[WrapperlessIcall]
	public static bool PasteComponentValues(Component component);

Here’s a simple script, that swaps the transforms of two objects, using CopyComponent and PasteComponentValues :slight_smile:

using UnityEngine;
using System.Collections.Generic;

public class TestMB : MonoBehaviour
{
	public GameObject go1;
	public GameObject go2;

	void OnGUI()
	{
		if (GUI.Button(new Rect(Screen.width / 2, 100, 200, 100), "Swap transforms")) {
			var t1 = go1.GetComponent<Transform>();
			var t2 = go2.GetComponent<Transform>();
			var temp = new GameObject("_TEMP").GetComponent<Transform>();

			// temp = t1;
			// t1 = t2;
			// t2 = temp;

			if (UnityEditorInternal.ComponentUtility.CopyComponent(t1)) {
				if (UnityEditorInternal.ComponentUtility.PasteComponentValues(temp)) {
					if (UnityEditorInternal.ComponentUtility.CopyComponent(t2)) {
						if (UnityEditorInternal.ComponentUtility.PasteComponentValues(t1)) {
							if (UnityEditorInternal.ComponentUtility.CopyComponent(temp)) {
								if (UnityEditorInternal.ComponentUtility.PasteComponentValues(t2)) {
									print("DONE");
								}
							}
						}
					}
				}
			}
			Destroy(temp.gameObject);
		}
	}

Enjoy! :smiley:


You can’t transfer a component from one object to another. What you can do is

  • add a new component to the new object and transfer all properties from one component to the other

  • or clone the whole object and remove what is not needed:

    FileComponent fileClone = (FileComponent)Instantiate(fileObj); //c#

First of all, setting fields/properties that aren’t public/readonly is pointless.
A much cleaner version of the code would be this. You really don’t need flags & the above codes are messed up with the type usage:

public static T AddComponent<T> ( this GameObject game, T duplicate ) where T : Component
{
    T target = game.AddComponent<T> ();

    foreach (PropertyInfo x in typeof ( T ).GetProperties ())
        if (x.CanWrite)
            x.SetValue ( target, x.GetValue ( duplicate ) );

    return target;
}

Then you just use it like this:

your_gameobject.AddComponent ( component_you_want_to_duplicate );

I command you on your implementation vexe, but allow me to one-up it. My biggest complaint is that it doesn’t allow for abstract types like Colliders to be copied. And when you copy their runtime types, the derived values are all omitted (e.g. isTrigger, sharedMaterial, etc.)

Introducing my own implementation, which takes care of these complaints and copies Derived fields flawlessly:

// Based on an implementation by vexe.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;

public static class RuntimeComponentCopier
{
    private const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default | BindingFlags.DeclaredOnly;

    public static T GetCopyOf<T>(this Component comp, T other) where T : Component
    {
        Type type = comp.GetType();
        if(type != other.GetType()) return null; // type mis-match

        Type derivedType = typeof(T);
        bool hasDerivedType = derivedType != type;

        IEnumerable<PropertyInfo> pinfos = type.GetProperties(bindingFlags);
        if(hasDerivedType)
        {
            pinfos = pinfos.Concat(derivedType.GetProperties(bindingFlags));
        }
        pinfos = from property in pinfos
                 where !(type == typeof(Rigidbody) && property.Name == "inertiaTensor") // Special case for Rigidbodies inertiaTensor which isn't catched for some reason.
                 where !property.CustomAttributes.Any(attribute => attribute.AttributeType == typeof(ObsoleteAttribute))
                 select property;
        foreach(var pinfo in pinfos)
        {
            if(pinfo.CanWrite)
            {
                 if(pinfos.Any(e => e.Name == $"shared{char.ToUpper(pinfo.Name[0])}{pinfo.Name.Substring(1)}"))
                {
                    continue;
                }

                try
                {
                    pinfo.SetValue(comp, pinfo.GetValue(other, null), null);
                }
                catch { } // In case of NotImplementedException being thrown. For some reason specifying that exception didn't seem to catch it, so I didn't catch anything specific.
            }
        }

        IEnumerable<FieldInfo> finfos = type.GetFields(bindingFlags);
        if(hasDerivedType)
        {
            if(finfos.Any(e => e.Name == $"shared{char.ToUpper(finfo.Name[0])}{finfo.Name.Substring(1)}"))
            {
                continue;
            }

            finfos = finfos.Concat(derivedType.GetFields(bindingFlags));
        }
        finfos = from field in finfos
                 where field.CustomAttributes.Any(attribute => attribute.AttributeType == typeof(ObsoleteAttribute))
                 select field;
        foreach(var finfo in finfos)
        {
            finfo.SetValue(comp, finfo.GetValue(other));
        }

        return comp as T;
    }

    public static T AddComponent<T>(this GameObject go, T toAdd) where T : Component
    {
        return go.AddComponent(toAdd.GetType()).GetCopyOf(toAdd) as T;
    }
}

If you already have vexe’s implementation, you can simply swap it out and it will still work the same, now with the addition of abstract types.

Usage

var col = GetComponent<Collider>(); // this is a Box Collider 
var copy = myComp.GetCopyOf(col); 
Debug.Log(copy.GetType()) // = BoxCollider

Or Simply

someGameObject.AddComponent(col); // is a Box Collider

Also doesn’t copy Obsolete values and checks if there is a shared version of a value before copying :smiley:

Unity has built-in methods to do as post above for editor, or see this page.
To use reflection is a good way too.

But actually, we might not ought to do this kind of generic stuff.

The reason is, we can’t know how those class writen by others works.
Maybe they use the variables in Awake() or what.
Copying any value of unknow class might cause unexpectable crash or behaviour.

I considered to write a generic copier, but canceled just 'coz this reason.
Then, let Unity do Unity stuff, and specific developer do theirs.
This’s a safer way…

I don’t think setting properties via reflection is the way to go, we should be setting the backing fields of the properties themselves, maybe it’s just me but this seems cleaner. With the vexes method we can only access private fields of the type explicitly declared, as well as any public fields (If we remove BindingFlags.DeclaredOnly).

We want to access inherited private fields, and to do this, we need to check each type explicitly.

public static T GetCopyOf<T>(this MonoBehaviour Monobehaviour, T Source) where T : MonoBehaviour
{
    //Type check
    Type type = Monobehaviour.GetType();
    if (type != Source.GetType()) return null;

    //Declare Binding Flags
    BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default | BindingFlags.DeclaredOnly;
            
    //Iterate through all types until monobehaviour is reached
    while (type != typeof(MonoBehaviour))
    {
        //Apply Fields
        FieldInfo[] fields = type.GetFields(flags);
        foreach (FieldInfo field in fields)
        {
            field.SetValue(Monobehaviour, field.GetValue(Source));
        }

        //Move to base class
        type = type.BaseType;
    }
    return Monobehaviour as T;
}

Just my two cents :), try it for yourself.

If there is anybody still struggling with this, I have made a few corrections to @CaseyHofland 's answer and it works great. Just copy/paste into a static class and you will be good to go!

    private const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default | BindingFlags.DeclaredOnly;

    public static T GetCopyOf<T>(this Component comp, T other) where T : Component
    {
        Type type = comp.GetType();
        if (type != other.GetType()) return null; // type mis-match

        List<Type> derivedTypes = new List<Type>();
        Type derived = type.BaseType;
        while(derived != null)
        {
            if(derived == typeof(MonoBehaviour))
            {
                break;
            }
            derivedTypes.Add(derived);
            derived = derived.BaseType;
        }

        IEnumerable<PropertyInfo> pinfos = type.GetProperties(bindingFlags);

        foreach (Type derivedType in derivedTypes)
        {
            pinfos = pinfos.Concat(derivedType.GetProperties(bindingFlags));
        }

        pinfos = from property in pinfos
                 where !(type == typeof(Rigidbody) && property.Name == "inertiaTensor") // Special case for Rigidbodies inertiaTensor which isn't catched for some reason.
                 where !property.CustomAttributes.Any(attribute => attribute.AttributeType == typeof(ObsoleteAttribute))
                 select property;
        foreach (var pinfo in pinfos)
        {
            if (pinfo.CanWrite)
            {
                if (pinfos.Any(e => e.Name == $"shared{char.ToUpper(pinfo.Name[0])}{pinfo.Name.Substring(1)}"))
                {
                    continue;
                }
                try
                {
                    pinfo.SetValue(comp, pinfo.GetValue(other, null), null);
                }
                catch { } // In case of NotImplementedException being thrown. For some reason specifying that exception didn't seem to catch it, so I didn't catch anything specific.
            }
        }

        IEnumerable<FieldInfo> finfos = type.GetFields(bindingFlags);

        foreach (var finfo in finfos)
        {

            foreach (Type derivedType in derivedTypes)
            {
                if (finfos.Any(e => e.Name == $"shared{char.ToUpper(finfo.Name[0])}{finfo.Name.Substring(1)}"))
                {
                    continue;
                }
                finfos = finfos.Concat(derivedType.GetFields(bindingFlags));
            }
        }

        foreach (var finfo in finfos)
        {
            finfo.SetValue(comp, finfo.GetValue(other));
        }

        finfos = from field in finfos
                 where field.CustomAttributes.Any(attribute => attribute.AttributeType == typeof(ObsoleteAttribute))
                 select field;
        foreach (var finfo in finfos)
        {
            finfo.SetValue(comp, finfo.GetValue(other));
        }

        return comp as T;
    }

    public static T AddComponent<T>(this GameObject go, T toAdd) where T : Component
    {
        return go.AddComponent(toAdd.GetType()).GetCopyOf(toAdd) as T;
    }

usage:

someGameObject.AddComponent(col);

This solution did not work for me on RectTransform, it returns nothing with any flags. I modified it to use PropertyInfo instead, to get it working.

void CopyComponent(Component component, GameObject target)
    {
        Type type = component.GetType();
        target.AddComponent(type);
        PropertyInfo[] propInfo = type.GetProperties(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance);
        foreach (var property in propInfo)
        {
            if (property.Name == "rect") continue;
            property.SetValue(target.GetComponent(type), property.GetValue(component, null), null);
        }
    }

Hi @vexe

I’m trying to use your code and works fine… as long as the type T is compiled statically

I’ve been trying to deep copy several MonoBehaviours from an object to another, but their type is not known at compile time. The Source GameObject holds several ActionBehaviour s, but the real case scenario is that on the Source there are different and multiple components, all having ActionBehaviour as a common ancestor class.

So I tried:

		GameObject _newBaseObj = newParent.gameObject;
		
		foreach (ActionBehaviour ab in actions)
		{
			System.Type _t = ab.GetType();
			//_newBaseObj.AddComponent(_t); //works of course but it's not calling the extension method
			_newBaseObj.AddComponent<_t>(ab); //throws a ActionReparentActions.cs(23,50): error CS0246: The type or namespace name `_t' could not be found. Are you missing a using directive or an assembly reference? exception
			Destroy(ab);
		}

action is a List, and contains the list of all Actions in the gameObject.
But, one Action could be ActionDisplayOverlay, or ActionDelay, or ActionMoveTo, all different subclasses. I’d like each of this components that have different fields to be deep copied (actually “moved”) to _newBaseObj, but I can’t see what I’m doing wrong, of if it is technically feasible.

Do you have any clue?

What if I want to iterate through one object’s components and add all of them to another object?
The extension doesn’t work when i try to do this:

var components = componentObject.GetComponents<Component>();
foreach (var component in components)
{
    objectToAddTo.AddComponent(component);
}

Just to add for anyone looking to copy an AudioSource.

If you want to copy the 3d Sound Settings its simply an animation curve.

AnimationCurve animationCurve = sourceAudioSource.GetCustomCurve(AudioSourceCurveType.CustomRolloff); targetAudioSource.SetCustomCurve(AudioSourceCurveType.CustomRolloff, animationCurve);