• Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
13
Question by vexe · Sep 04, 2013 at 05:39 AM · c#gameobjectgetcomponentcomponentaddcomponent

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.

Comment
Add comment · Show 3
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image getyour411 · Sep 04, 2013 at 06:33 AM 0
Share

on that last example, try putting fileScript in quotes

 myOtherObj.AddComponent("fileScript");

(assu$$anonymous$$g that is actually the name of your script sans the .js)

avatar image vexe · Sep 04, 2013 at 06:39 AM 1
Share

I don't use JS. There's a C# tag. But from time to time I use the var keyword. Does this also work for C#? - I'll give it a try...

avatar image vexe · Sep 04, 2013 at 06:41 AM 1
Share

wait, do you mean that "fileScript" should be exactly what I'm adding? - that's not gonna work cuz I don't know what I'm gonna be picking up, could be a "TextFile", "AudioFile" etc.

12 Replies

· Add your reply
  • Sort: 
avatar image
58
Best Answer

Answer by vexe · Feb 16, 2014 at 10:25 AM

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 :D

Comment
Add comment · Show 12 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Wolfram · Jul 01, 2015 at 11:27 AM 0
Share

great answer!

avatar image RimFra · Sep 01, 2015 at 12:16 PM 0
Share

Would it be possible to have it recursive?Nested classes are not copied.

avatar image M0rph3v5 · Dec 02, 2015 at 10:22 PM 1
Share

Just stumbled into a problem with this code, things like an EdgeCollider2D which extends a Collider2D properties like offset won't get copied. If I remove the flags it works, but I'm not sure what impact that has. Not sure either under what flag it would be.

avatar image Llockham-Industries · May 21, 2017 at 11:13 PM 1
Share

Just checking out the documentation. It appears BindingFlags.DeclaredOnly restricts the members gathered to the members of that type only, and not members inherited. Not sure why it's included, removing that should allow you to copy members of inherited types as well.

avatar image TommyZakh · Oct 29, 2017 at 01:06 AM 0
Share

This does not work when copying AudioSource components. If I try to do this, it gives the error message: "$$anonymous$$Volume is not supported anymore. Use $$anonymous$$-, maxDistance and rolloff$$anonymous$$ode ins$$anonymous$$d. System.Reflection.$$anonymous$$onoProperty:GetValue(Object, Object[])" and several more similar ones. I assume this is because it tries to copy deprecated properties, but how would I go about ignoring them?

avatar image Cardinal90 TommyZakh · Aug 23, 2018 at 06:07 AM 1
Share

I had the same problem. The solution I found is to add this code before try-catch block

 bool obsolete = false;
 IEnumerable attrData = pinfo.CustomAttributes;
 foreach (CustomAttributeData data in attrData)
 {
     if (data.AttributeType == typeof(System.ObsoleteAttribute))
     {
         obsolete = true;
         break;
     }
 }
 if (obsolete)
 {
     continue;
 }

avatar image CaseyHofland Cardinal90 · Apr 12, 2020 at 04:35 PM 1
Share

Try this ins$$anonymous$$d:

 using System.Linq;
 
 // Replace pinfos with this:
 var pinfos = from property in type.GetProperties(flags)
          where !property.CustomAttributes.Any(attribute => attribute.AttributeType == typeof(ObsoleteAttribute))
          select property;

It queries the properties before iterating over them.

Show more comments
avatar image
3

Answer by robhuhn · Sep 04, 2013 at 06:45 AM

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#

Comment
Add comment · Show 3 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image vexe · Sep 04, 2013 at 06:48 AM 1
Share

Yes, unfortunately that's the conclusion I came to as well... :/ I don't know why Unity doesn't allow this, it should be simple. Thanks for the answer. I'll wait a bit and then mark it as the correct answer.

avatar image robhuhn · Sep 04, 2013 at 07:00 AM 0
Share

$$anonymous$$ay be because of the circular dependencies of an object but that's just a guess.

avatar image vexe · Sep 04, 2013 at 08:10 AM 1
Share

but hey wait a second, in the editor, you can copy a component and paste it somewhere else, can't we do that in code??? that would be cool!

avatar image
12

Answer by vexe · Dec 27, 2013 at 02:40 PM

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 :)

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

alt text

Enjoy! :D



swap.png (293.1 kB)
Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image domiii · Feb 27, 2016 at 12:34 PM 0
Share

This is the true answer (given, you are in the editor that is).

avatar image
1

Answer by Wanzyee · Jul 24, 2015 at 07:53 AM

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

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Wanzyee · Nov 11, 2015 at 07:00 AM 0
Share

Finally I integrated Unity built-in API to do this with an editor window GUI very carefully...cheers!

avatar image
0

Answer by fogsight · Jun 22, 2016 at 05:13 AM

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);
         }
     }
Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
  • 1
  • 2
  • 3
  • ›

Your answer

Hint: You can notify a user about this post by typing @username

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

Welcome to Unity Answers

If you’re new to Unity Answers, please check our User Guide to help you navigate through our website and refer to our FAQ for more information.

Before posting, make sure to check out our Knowledge Base for commonly asked Unity questions.

Check our Moderator Guidelines if you’re a new moderator and want to work together in an effort to improve Unity Answers and support our users.

Follow this Question

Answers Answers and Comments

31 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

AddComponent for RawImage not functioning as expected 0 Answers

error CS0118: `New_Career.Fame' is a `field' but a `type' was expected 1 Answer

GetComponent of ALL clones? 2 Answers

Find GameObjects with a true boolean and put them in an array? 1 Answer

Problem with getting component, help please? ^^' 2 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges