- Home /
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?
FWIW, there are a couple tings in the asset store along the lines of "copy cool s*** at run time!" Get them and see if they're any good for you.
Answer by Shaffe · Dec 04, 2013 at 12:22 PM
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 was able to use your generic version quite successfully, with the addition of the BindingFlags bitmask to the getFields function ( GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic) )
However, this only seems to work for components created in the project - not for build in components such as BoxCollider. Any ideas on how we can improve the method to also handle these components?
The answer here handles built-in components: http://answers.unity3d.com/questions/530178/how-to-get-a-component-from-an-object-and-add-it-t.html
@DAVco I think you need to add BindingFlags.FlattenHierarchy to get fields from inherited types.
thanks for this! had an odd case where prefabs weren't appropriate and needed to verify that an artist had correct components configured properly.
Thanks!
This didn't work for ParticleSystemRenderer component (so I guess for any ParticleSystem**** components). I know it's a bit different in nature but Unity 5 has made many particle system properties independent 'components' which were earlier accessible from ParticleSystem component, now you need to call eg. GetComponent< ParticleSystemRenderer >(). property. Interesting however, that the version of @vladipus works for these components too (throws an error message but seem to finish copying all properties).
Answer by turbanov · Dec 28, 2015 at 02:46 PM
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;
}
Only this solution worked for ParticleSystemRenderer component in Unity 5!
In editor mode it gives a warning when it copies any material property that this can result in memory leaks and copying shared$$anonymous$$aterial is sufficient. So for that one can replace line 15 to :
if (!prop.CanWrite || !prop.CanWrite || prop.Name == "name" || prop.PropertyType.Equals(typeof($$anonymous$$aterial)) || prop.PropertyType.Equals(typeof($$anonymous$$aterial[]))) continue;
and we can just set manually after calling your function:
sourceComp.material = targetComp.shared$$anonymous$$aterial;
sourceComp.materials = targetComp.shared$$anonymous$$aterials;
or just place the replacement code inside your loop.
For copying an audiosource in unity 2018.3, I got the error $$anonymous$$Volume is not supported anymore. Use $$anonymous$$-, maxDistance and rolloff$$anonymous$$ode ins$$anonymous$$d. Which I reconciled via this link. So just add
if(!propertyInfo.IsDefined(typeof(ObsoleteAttribute), true))
propertyValue = propertyInfo.GetValue(myComponent,null);
Answer by Bunny83 · May 17, 2013 at 11:30 AM
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.
I would of course do that if I would not like to have it as a mesh particle emitter... which requires me to add the components to the object that should emit the particles. And it is not only this case. It's not the first time that I wish I could copy a component with all it's values, it would just be very useful ;)
Answer by wa1bo · Nov 22, 2014 at 02:36 PM
For editor only scripts, you can use: EditorUtility.CopySerialized
For runtime, you can use SerializedObject.CopyFromSerializedProperty
(iterate on the properties, and applying each one)
Saved my day that EditorUtility.CopySerialized
Didn't come across that in Unity $$anonymous$$anual or Google searches.
Answer by SuperSboy · Apr 18, 2017 at 09:15 AM
UnityEditorInternal.ComponentUtility.CopyComponent(original); UnityEditorInternal.ComponentUtility.PasteComponentAsNew(destinationObject);
Your answer
Follow this Question
Related Questions
Accesing components on multiple objects in scene 3 Answers
Can a script enable components? 2 Answers
Two AudioSource Components on one Gameobject? 1 Answer
Define game object. 0 Answers
Missing Components error 0 Answers