Copy a component at runtime

I need to copy a component with all its values at runtime (would save me a lot of time), but I don’t know how to do that. Google only brings results where people want to copy a component in editormode, which is available in Unity4 anyway.

My use-case: I have several objects and I want to make them burnable. For that, I would like to copy the particle components (legacy) of my fire-prefab and add them to every object I mark as “burnable” in a script, so I can switch on the particleemitter by code if it should start burning. I could do it by hand in editor, but that’s rather time-consuming. So, is there a way to do that easily by code?

Sometimes a prefab is not suitable.

This method allows to copy fields values on a new component using the reflection API.

Component CopyComponent(Component original, GameObject destination)
{
    System.Type type = original.GetType();
    Component copy = destination.AddComponent(type);
    // Copied fields can be restricted with BindingFlags
    System.Reflection.FieldInfo[] fields = type.GetFields(); 
    foreach (System.Reflection.FieldInfo field in fields)
    {
       field.SetValue(copy, field.GetValue(original));
    }
    return copy;
}

And here the version with generic typing:

T CopyComponent<T>(T original, GameObject destination) where T : Component
{
	System.Type type = original.GetType();
	Component copy = destination.AddComponent(type);
	System.Reflection.FieldInfo[] fields = type.GetFields();
	foreach (System.Reflection.FieldInfo field in fields)
	{
		field.SetValue(copy, field.GetValue(original));
	}
	return copy as T;
}

I’ve edited the Shaffe’s version to utilize properties and check for static variables, component reuse instead of creating new. I’m currently using this technique in the editor, however.

    T CopyComponent<T>(T original, GameObject destination) where T : Component
    {
        System.Type type = original.GetType();
        var dst = destination.GetComponent(type) as T;
        if (!dst) dst = destination.AddComponent(type) as T;
        var fields = type.GetFields();
        foreach (var field in fields)
        {
            if (field.IsStatic) continue;
            field.SetValue(dst, field.GetValue(original));
        }
        var props = type.GetProperties();
        foreach (var prop in props)
        {
            if (!prop.CanWrite || !prop.CanWrite || prop.Name == "name") continue;
            prop.SetValue(dst, prop.GetValue(original, null), null);
        }
        return dst as T;
    }

You should create a prefab of your particle effect and simply Instantiate it at runtime and attach it as child object.

Components are always bound to the GameObject they’re attached to. The only way is to create the same component on your target GameObject and copy all variables manually. Note: this doesn’t work with all components because some have internal variables which you can’t access from outside.

For editor only scripts, you can use:
EditorUtility.CopySerialized

For runtime, you can use SerializedObject.CopyFromSerializedProperty (iterate on the properties, and applying each one)

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…

Just changing to look for a copy of the component int he target object, else Unity will crash trying to add the same component twice - if it is found, it writes over the component.

    public static Component CopyComponent(Component original, GameObject destination)
    {
        Component[] m_List = destination.GetComponents<Component>();
        System.Type type = original.GetType();
        System.Reflection.FieldInfo[] fields = type.GetFields();

        foreach (Component comp in m_List)
        {
            // If we already have one of them
            if (original.GetType() == comp.GetType())
            {
                foreach (System.Reflection.FieldInfo field in fields)
                {
                    field.SetValue(comp, field.GetValue(original));
                }
                return comp;
            }
        }

        // By here, we need to add it
        Component copy = destination.AddComponent(type);

        // Copied fields can be restricted with BindingFlags
        foreach (System.Reflection.FieldInfo field in fields)
        {
            field.SetValue(copy, field.GetValue(original));
        }

        return copy;
    }

I was recently tasked with a similar problem here and I must say that I do agree that it probably isn’t great practice to create things dynamically that are so important. It gets really hard to debug the problem when everything has been dynamically created. Imagine a confused developer on your team spending hours trying to find a prefab that was spawned in when it really doesn’t exist in the first place?

I solved this problem by thinking about it differently. Instead of copying components during run-time, I decided to think about syncing my prefabs that will be spawned in at editor time so that they have essentially identical components. Using this mindset, I was able to create a “template” prefab that houses all my components that I want and then I created a unity “AssetModificationProcessor” that sniffed for my template to be modified and saved. Once it is saved, I scrape up all the prefabs that I want to sync to it and just add all my components and use “UnityEditor.EditorUtility.CopySerialized” to copy data from the template component to the new or existing component.

This basically means that every time another developer on my team modifies the template, all the components on the template will then be copied over to my player prefabs while in-editor.

This allows me to define the set of components one time and mirror that into multiple prefabs at editor time so that my objects all spawn with identical components at runtime :).

Here is an adaptation of my script for reference (note that the paths don’t map to real things. You would have to change the prefab/asset paths in your environment to see this work):

using System.Collections.Generic;
using UnityEditor;
using UnityEditor.VersionControl;
using UnityEngine;

public class FileModificationWarning : UnityEditor.AssetModificationProcessor
{
    static string[] OnWillSaveAssets(string[] paths)
    {
        List<string> pathsToSave = new List<string>();
        pathsToSave.AddRange(paths);

        for (int i = 0; i < paths.Length; i++)
        {
            // ZAS: we only care about our special prefab. Not anything else
            if (paths*.Contains("Prefabs/VisualEffects"))*

{
var visualEffectsPrefab = AssetDatabase.LoadAssetAtPath(paths*);*
if (visualEffectsPrefab != null)
{
SynchronizeVisualEffectsToPlayerPrefabs(visualEffectsPrefab as GameObject, pathsToSave);
}
else
{
Debug.LogErrorFormat(“Failed to load visual effects prefab at path {0}”, paths*);*
}
}
}

return pathsToSave.ToArray();
}

private static void SynchronizeVisualEffectsToPlayerPrefabs(GameObject visualEffectsPrefab, List pathsToSave)
{
List errors = new List();
if (SychronizePlayerEyeWithVisualEffectPrefab(“PlayerA”, visualEffectsPrefab, errors)) { pathsToSave.Add(“PlayerA”); }
if (SychronizePlayerEyeWithVisualEffectPrefab(“PlayerB”, visualEffectsPrefab, errors)) { pathsToSave.Add(“PlayerB”); }
if (SychronizePlayerEyeWithVisualEffectPrefab(“PlayerC”, visualEffectsPrefab, errors)) { pathsToSave.Add(“PlayerC”); }
if (SychronizePlayerEyeWithVisualEffectPrefab(“PlayerD”, visualEffectsPrefab, errors)) { pathsToSave.Add(“PlayerD”); }

if (errors.Count > 0)
{
for (int i = 0; i < errors.Count; i++)
{
EditorUtility.DisplayDialog(“Sync Error”, errors*, “Ok”);*
}
}

EditorUtility.DisplayDialog(“Sync visual changes to prefabs”, “All player prefabs were modified because a change to the visual asset prefab was detected. Remember to check in the player prefabs!”, “Ok!”);
}

private static bool SychronizePlayerEyeWithVisualEffectPrefab(string playerPrefabPath, GameObject visualEffectsPrefab, List errors)
{
var potentialPlayerPrefab = Resources.Load(playerPrefabPath) as GameObject;
if (potentialPlayerPrefab == null)
{
errors.Add(string.Format(“{0}: Failed to load prefab”, playerPrefabPath));
return false;
}

// ZAS: if you use version control then we need to check out the file we are changing first
if (Provider.hasCheckoutSupport)
{
Provider.Checkout(potentialPlayerPrefab, CheckoutMode.Asset);
}

// ZAS: get all components so we can iterate through and copy each one
var components = visualEffectsPrefab.GetComponentsInChildren(true);
for (int i = 0; i < components.Length; i++)
{
// ZAS: everything has a transform on it. If you want to sync this then just remove this statement and let it pull the serialized data :slight_smile:
if (components is Transform)
{
continue;
}

// ZAS: lets find the component first and just use that if we find it
var foundComponents = potentialPlayerPrefab.GetComponentsInChildren(components*.GetType(), true);*
Component existingComponent = null;
if(foundComponents.Length > 0)
{
existingComponent = foundComponents[0];
}

// ZAS: remove other instances to make sure there is only one!
if(foundComponents.Length > 1)
{
for (int j = 1; j < foundComponents.Length; j++)
{
Component.DestroyImmediate(foundComponents[j], true);
}
}

// ZAS: did not find a component… time to add a new one!
if (existingComponent == null)
{
existingComponent = potentialPlayerPrefab.AddComponent(components*.GetType());*
}

// ZAS: copy the serialied data over from our template to our new or existing component (this is where all serialized fields are synchronized)
EditorUtility.CopySerialized(components*, existingComponent);*
}

// ZAS: we want to make sure that our changes cause the assets to be marked for saving
EditorUtility.SetDirty(potentialPlayerPrefab);
return true;
}
}
Just thought I would drop by and share :slight_smile:

UnityEditorInternal.ComponentUtility.CopyComponent(original);
UnityEditorInternal.ComponentUtility.PasteComponentAsNew(destinationObject);