Search Unity

Copying components at runtime, using reflection

Discussion in 'Scripting' started by Nanako, May 31, 2015.

  1. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    I'm currently trying to implement the accepted solution on this page: http://answers.unity3d.com/questions/530178/how-to-get-a-component-from-an-object-and-add-it-t.html

    My overall goal, is to create an authortime script which i can use to copy components en-masse, between hierarchies of objects (ragdolls, basically). Prefabs aren't suitable for my purpose

    The main code block there doesn't compile by default;

    Code (CSharp):
    1.  public static T GetCopyOf<T>(this Component comp, T other) where T : Component
    2. {
    3.      Type type = comp.GetType();
    4.      if (type != other.GetType()) return null; // type mis-match
    5.      BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default | BindingFlags.DeclaredOnly;
    6.      PropertyInfo[] pinfos = type.GetProperties(flags);
    7.      foreach (var pinfo in pinfos) {
    8.          if (pinfo.CanWrite) {
    9.              try {
    10.                  pinfo.SetValue(comp, pinfo.GetValue(other, null), null);
    11.              }
    12.              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.
    13.          }
    14.      }
    15.      FieldInfo[] finfos = type.GetFields(flags);
    16.      foreach (var finfo in finfos) {
    17.          finfo.SetValue(comp, finfo.GetValue(other));
    18.      }
    19.      return comp as T;
    20. }
    I added "using System.Reflection;" to the top of the script, and thar got rid of the errors about binding flags, but i'm still getting another compile error at the very first "Type"

    "The type or namespace name "Type" could not be found"

    Presumably this means i'm missing another include at the top, but i'm not sure what to include.
    Help me make this work ;-;
     
    krisu likes this.
  2. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
    The type Type is in the namespace System, so you will need a
    Code (csharp):
    1. using System;
    As you have probably found, the unity docs only go so far. That is because Unity is just a small framework compared the whole of .net. You will probably have some luck looking up things that don't appear in the Unity docs by googling "msdn + your search keyword". This should pull up the Microsoft documentation on the .net framework, which by-and-large is available when scripting Unity in C#.
     
    Kiwasi and Nanako like this.
  3. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    hooray, that's a step forward

    now how do i use this:

    Code (CSharp):
    1.  public static T AddComponent<T>(this GameObject go, T toAdd) where T : Component
    2. {
    3.      return go.AddComponent<T>().GetCopyOf(toAdd) as T;
    4. }
    where am i supposed to put this code, i assume it overrides some method of gameobject?

    trying to use it in the same script just fails to compile

    "T does not contain a definition for "GetCopyOf" and no extension method "GetCopyOf" accepting a first argument of type 'T' could be found."

    This makes sense looking at it. Isn't it being fed incorrect arguments?
    there's another usage example in the same post that has the same issue.
    I'm a little out of my depth here ;-; but i just want to make this compile,
     
  4. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
    I would like to post more, but I'm really short on time.

    Basically, Extension methods are a trick added in .net 3.5 which allow programmers to add methods to classes without extending them through inheritance. In order to use an extension method, it must be declared as a static method inside a static class. You will then need to add the namespace of this static class anywhere you would like to use this extensions method.

    Have a look here: https://msdn.microsoft.com/en-us/library/bb383977.aspx
     
  5. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
    An example, though excuse the "coding form the hip". I don't have time to compile check it ;)

    Code (csharp):
    1. namespace MyExtensions
    2. {
    3.   public static class StringExtensions
    4.   {
    5.     public static string Reverse(this string stringToReverse)
    6.     // The "this" keyword above indicates to the compiler that this is an extension method and it applies to the type string
    7.     {
    8.       char[] charArray = s.ToCharArray();
    9.       Array.Reverse( charArray );
    10.       return new string( charArray );
    11.     }
    12.   }
    13. }
    Code (csharp):
    1. using MyExtensions
    2.  
    3. public class MyClass
    4. {
    5.   public void PrintReverse(string stringToReverse)
    6.   {
    7.     Console.WriteLine(stringToReverse.Reverse());
    8.   }
    9. }
     
  6. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    hello again :D

    i;m still getting this working, i've got those functions compiling (though untested, as of yet) but ive got another problem which i think is related enough to ask here;

    how do i use component types?

    More specifically, I'm trying to make some modifications to joints, before i copy them over to the destination object. At this point i've already gathered a 2D list of components in my hierarchy, generically typed as Component. And what i now want to do is check if any specific component in there is a joint of some sort. This is as far as i've gotten:

    Code (CSharp):
    1.         for (int i = 0; i < components.Count; i++)
    2.         {
    3.             foreach (Component component in components[i])
    4.             {
    5.                 if (component.GetType() == FixedJoint )
    6.                 {
    7.  
    8.                 }
    9.             }
    10.         }
    That won't compile. I'm not really clear what to do with the return value of Component.GetType()
    can you offer any clarification?

    Once i figure that out, i was then going to check if it value == FixedJoint || value == CharacterJoint, etc, for every joint type. But is there a faster way to do that, just to know if it corresponds to any type of joint at all?
     
  7. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    You can use A)
    Code (csharp):
    1. if (component is SomeType)
    or B)
    Code (csharp):
    1. if (component.GetType() == typeof(SomeType) )
    There's a subtle difference between the two, mostly to do with inheritance: if component inherits from SomeType, A will return true but B will return false.

    It seems like you want everything that inherits from Joint, so you'll want to use
    Code (csharp):
    1. if (component is Joint)
     
    Nanako likes this.
  8. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
    Okay, at this point you are starting to do type analysis.

    First of all, the reason this won't compile is because you are comparing the return value of GetType(), which is an object of the type Type, to FixedJoint, which I'm guessing is the name of a class. Instead, you need to get an instance of an object of type Type which refers to FixedJoint. To do this, you can either create a new FixedJoint and call GetType() on it or, the preferred method, use the keyword typeof to have the compiler create a Type object without needing an instance.

    Code (csharp):
    1. if (component.GetType() == typeof(FixedJoint) )
    Type comparison is a little bit confusing at first because it relates to reflection, the runtime analysis of code. You're basically trying to teach the computer to do what programmers do in their head.

    As for whether this is the best approach.. It usually is not. If you are using reflection it's worth reconsidering at least once. So, to proceed, what is it you're trying to do with the components once you know which type it is?
     
    Kiwasi likes this.
  9. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    thank you guys :D
    if (component is Joint) seems to be working well, that's perfect.

    Next question!

    Code (CSharp):
    1.         for (int i = 0; i < components.Count; i++)
    2.         {
    3.             foreach (Component component in components[i])
    4.             {
    5.                 destinationBones[i].gameObject.AddComponent<Component.GetType()>(component);
    6.                
    7.                
    8.                
    9.                 if (component is Joint)
    10.                 {
    11.                     //Some stuff goes here
    12.                 }
    13.             }
    14.         }
    Why won't this compile? destinationBones is a list of transforms, (i also have a list of source bones which i used to compile the components list)

    I'm pretty sure the type is the problem here but i'm not sure of the correct syntax to make that work
     
  10. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    As mentioned, i'm using this to copy ragdoll settings. Copying over all components between identically named bones in two hierarchies.
    Once i identify a joint component, i need to do something additional after copying it over. That is, i need to set it's attachedBody to the equivilant bone's rigidbody in the new hierarchy. Because if i copy it without doing that, it's still going to be stuck to the old ragdoll instead of being a seperate entity, which will cause a catastrophic mess.

    I think this is the only change i'll need to make in the cloning process, but if i know how to identify a component's type (and now i do) then i can make other changes as necessary.
     
  11. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
    Okay. In regards to the bones and ragdoll settings, it's my turn to be out of my depth. However, I can explain why your code doesn't compile and what I meant by "it is usually not (the best approach).

    The trouble is inside your angle brackets <>. In C#, when used like this - after a method name - they are denoting that the method is generic. That is, it can accept (almost, typically) any type of object and the angle brackets allow the programmer to specify what type of object they will be providing in this circumstance. There are various reasons for using generics.

    One is that they are typically a bit faster because they can be type restrictive which allows the compiler to make additional optimizations.

    However, the more useful (in my opinion) reason is because it allows the code to be more readable and prevents unnecessary casting. I see that the Unity docs for the AddComponent() method doesn't include details regarding the generic form and instead just points you towards generic (haha) documentation.

    So, if we didn't know about generic methods, we could specify a type to add using the types name:
    Code (csharp):
    1. destinationBones[i].gameObject.AddComponent("Joint");
    I find this super weird because a string is very undescriptive and there is a perfectly good alternative which you are already familiar with called Type. I would have expected the signature of this method to take a Type so you could do something like:
    Code (csharp):
    1. destinationBones[i].gameObject.AddComponent(typeof(Joint));
    or in your case
    Code (csharp):
    1. destinationBones[i].gameObject.AddComponent(component.GetType());
    Anyways, they choose a string, so we could do:
    Code (csharp):
    1. destinationBones[i].gameObject.AddComponent(component.GetType().Name);
    I noticed that you are attempting to call AddComponent with an instance of the type of component you are adding. The documentation doesn't show that this is possible, so I'm not sure if that's a misunderstanding or you have access to a different library. It seems to me that AddComponent creates a new, fresh component and can't accept a component that was created by hand.

    So in summary, I think this will work for you:
    Code (CSharp):
    1.         for (int i = 0; i < components.Count; i++)
    2.         {
    3.             foreach (Component component in components[i])
    4.             {
    5.                 destinationBones[i].gameObject.AddComponent(component.GetType().Name);            
    6.              
    7.                 if (component is Joint)
    8.                 {
    9.                     //Some stuff goes here
    10.                 }
    11.             }
    12.         }
    Now.. Why do I think the type casting is unnecessary?
    You say that you need to know when you are dealing with a Joint component. If, under different circumstances, you had control over all the possible types of components, you could have them implement a certain interface. Say IJoinable. IJoinable would define a method which could be sued to do additional tasks after being copied over. For instance, say after copying the Joint over, you wanted to change its springiness (a made up property and as far as I know a made up word)

    Code (csharp):
    1. interface IJoinable
    2. {
    3.   public AfterAttached(GameObject attachedTo);
    4. }
    5.  
    6. class Join : IJoinable
    7. {
    8.   //...
    9.   public AfterAttached(GameObject attachedTo)
    10.   {
    11.     this.Springiness = 10;
    12.   }
    13.   //...
    14. }
    Now in your code, you could call component.AfterAttached in order to have your type do the Springiness setting.
    Code (CSharp):
    1.         for (int i = 0; i < components.Count; i++)
    2.         {
    3.             foreach (Component component in components[i])
    4.             {
    5.                 var addedJoint = destinationBones[i].gameObject.AddComponent(component.GetType().Name) as Joint;
    6.                 addedJoint.AfterAttached(destinationBones[i]);
    7.             }
    8.         }
    And you would code the other Components to implement IJoinable but do nothing when AfterAttached was called.

    Anyways, this is not really applicable to your scenario because, I'm guessing, you don't own the source code for Joint or the other types of components your enumeration might find. Further, a bit of type casting is not necessarily bad, I just like to review it whenever it comes up but it's not always necessary and not always easily understood. Reflection is usually considered an advanced topic.

    Speaking of your enumeration. Is there a reason you use a for loop in the first case and a foreach loop in the second? I would prefer foreach in both cases.

    Also, I think you'll find LINQ very interesting though it is a huge topic in itself. LINQ is a feature that was introduced in .Net 3.5 and includes a number of extension methods on the basic IEnumerable. One of the methods which might help you out is OfType<>();. Let me explain:

    When called on a generic Enumerable, OfType returns only the objects in the Enumeration that are of a particular Type. So you could use it like so to reduce the number of possible Component's you are dealing with in your foreach loop.

    Code (csharp):
    1.  
    2. foreach (Component component in components[i].OfType<Joint>())
    3. {
    4.   destinationBones[i].gameObject.AddComponent(component.GetType().Name);
    5.  }
     
  12. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    Indeed you have misunderstood
    Referencing back to my original post, i am attempting to implement a solution which someone else wrote. That is, the (green highlighted) accepted answer posted by vexe, on this page http://answers.unity3d.com/questions/530178/how-to-get-a-component-from-an-object-and-add-it-t.html

    His solution includes an extension method for addcomponent, which makes this operation possible (passing a reference in order to create a copy of that component). And i think THAT part of the code is working fine.

    The usage example is
    gameObject.AddComponent<Type>(reference);

    And since i am pulling my component reference from a list of type Component, i need to insert the type of the component into the angle brackets, as well as the reference to it. i previously tried to put GetType() inside the angle brackets, and that didn't work.

    Why doesn't that work. And how exactly can i extract my component's type and then feed it into there?
     
  13. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    why yes, there is.

    Code (CSharp):
    1.             for (int i = 0; i < components.Count; i++)
    2.             {
    3.                 foreach (Component component in components[i])
    4.                 {
    5.                     destinationBones[i].gameObject.AddComponent<Component.GetType()>(component);
    6.                  
    7.                  
    8.                  
    9.                     if (component is Joint)
    10.                     {
    11.                         //Some stuff goes here
    12.                     }
    13.                 }
    14.             }
    15.  
    you might notice that my for loop is using an index integer, i
    And that same integer is then used again, in the foreach loop.
    i defines which element of the first dimension of the list, that we're currently looking at.

    I'm sure there are other ways to accomplish that, but this is just the way that made most sense to me. it feels elegant.
     
  14. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Reflection is fun! Just browsing through this there are a bunch of pitfalls waiting to bite you. @eisenpony is doing a good job explaining the cut and thrust of how reflection works, so I'll leave that side to him.

    This is a relatively shallow copy. As you have seen all of your variables are set to the same value as the previous game object. This applies for all reference types, but not to value types. So you are going to have some weird mirroring effects.

    Awake will also run before you change the values. Start will run after. Totally manageable, just be aware of it.

    Any side effects of setting properties will occur.

    Nothing that can't be dealt with. But you'll see why reflection is normally considered only as a last resort.
     
    Nanako likes this.
  15. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
    Okay, my image is getting clearer. Seems like every time I've attempted to respond to this post I've been strapped for time so, I apologize for my omissions.

    So I said I would explain why that code snippet wouldn't compile and then I didn't. Oops.

    Think of the bit inside the angle bracket this way: it is a hint to the compiler. Like I said before, one of the purposes of generic programming is to provide the compiler more information so that it can make extra optimizations or handle type safety for us. When we write a generic class or method, we use a variable for the type because we want the code to be suitable to more than one type. However, when we use the generic class or method, we need to decide on a specific type and give a hint to the compiler inside of the angle brackets. This is the crux of your problem: it is the compiler that needs the information inside the angle brackets but you are using .GetType() which returns an instance of a Type object at runtime.

    By the time GetType() is called, it's far too late to be any use to the compiler. So you're left with a catch-22. You need the type to help the compiler out but you need the compiled code to get the type.

    Let's go back to why we need to know the type in the first place: I already mentioned these two: optimization and type safety. Now I'll add one more: the author of the generic code might want to know certain things about the type. Well, since we have the source for the generic method you are implementing we can analyze it.

    Let's talk about GetCopyOf<T>(T other) first.

    First of all, I see they author has put a restriction on the types of objects that can be passed in:
    Code (csharp):
    1. public static T GetCopyOf<T>(this Component comp, T other) where T : Component
    where T : Component says that this method will only accept objects of type Component. Anything else will cause a compile time error.

    Next, the Author immediately uses .GetType() to determine the runtime type of the object coming in.
    Code (csharp):
    1. Type type = comp.GetType();
    2. if (type != other.GetType()) return null;
    We already know that it has to be of type Component, but GetType() will tell us which specific implementation of Component, that is the type of class that inherited from Component, we have.

    At this point, the author doesn't seem to care about what type is passed in. They treat it in a very generic way, simply asking for the first instance's properties and then setting the properties of the second.

    I would say we are okay to use any type so long it is a Component. It's perfectly okay to call GetCopyOf like this:
    Code (csharp):
    1. component.GetCopyOf<Component>(otherComponent);
    And it doesn't matter if component and otherComponent are actually Joint Components, so long as they have the same type.

    A point of interest:
    since the signature of this method looks like: GetCopyOf<T>(T otherComponent), the compiler can get its hint without the angle brackets. That's because it will know what type otherComponent is at compile time and it can then deduce that T = T. So we can make the call even simpler:
    Code (csharp):
    1. component.GetCopyOf(otherComponent);

    Now let's deal with the second extension method which is the one you are actually trying to use:

    Code (csharp):
    1. public static T AddComponent<T>(this GameObject go, T toAdd) where T : Component
    2. {
    3.   return go.AddComponent<T>().GetCopyOf(toAdd) as T;
    4. }
    In this code, the author makes use of the generic method AddComponent<T>(), so now things get interesting. In order to use this method, we need to know the very specific type of component we want to add because AddComponent<T>() does the instantiation of that component for us. Unfortunately, in your situation, we simply can't know the type at compile time unless we use type checking for each possible type. Something like:
    Code (csharp):
    1. for (int i = 0; i < components.Count; i++)
    2. {
    3.   foreach (Component component in components[i])
    4.   {              
    5.     if (component is Joint)
    6.     {
    7.       destinationBones[i].gameObject.AddComponent<Joint>(component);
    8.     } else if (component is FixedJoint)
    9.       destinationBones[i].gameObject.AddComponent<Joint>(component);
    10.     //etc...
    11.   }
    12. }
    However, I don't really like to dirty my code up with all those if and else ifs. It makes our code ugly and it also creates a dependency for this code on all the possible types of Components we want to handle. This defeats the purpose of generic programming.

    So, here's an alternative. Let's change the authors implementation of AddComponent<T>() to make it look more like the non-generic flavor of AddComponent().
    Code (csharp):
    1. public static Component AddComponent(this GameObject go, Component toAdd)
    2. {
    3.   return go.AddComponent(toAdd.GetType().Name).GetCopyOf(toAdd);
    4. }
    Also, I'm looking more carefully at the documentation for AddComponent. Wow is it bad! I can't tell what the return type is and I think there actually might be an overload for this method which takes the strongly qualified type Type instead of a string for the class name, so let's try this so that we don't need to access the .Name.
    Code (csharp):
    1. public static Component AddComponent(this GameObject go, Component toAdd)
    2. {
    3.   return go.AddComponent(toAdd.GetType()).GetCopyOf(toAdd);
    4. }
    Now we can use our new extension method like so:
    Code (csharp):
    1. for (int i = 0; i < components.Count; i++)
    2. {
    3.   foreach (Component component in components[i])
    4.   {
    5.     destinationBones[i].gameObject.AddComponent(component);
    6.   }
    7. }
     
    twobob and Nanako like this.
  16. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
    Okay, so there's been this talk about not needing reflection and I'd like to show you how you could do this without it.

    The author's code is pretty good because it will dynamically analyze the type that comes in and copy each public and private instance property and field. To me, there are some red flags in this code though.

    First of all, I hate seeing comments that go something like this "for some reason ... so I did ..." That's just a personal pet peeve though..

    Second, they are bit-wise "oring" BindingFlags.Default into their other BindingFlags options. This is kind of like multiplying your algebra answer by one when you have finished solving it. To me it says: I don't really know what's going on here but I saw someone else do this.

    Finally, they put their property setting code in a try catch block but not their field setting code. The try catch is smart because some properties and fields are declared as read only. If you try to set a read only property or field, you will generate an Exception. In the case of a property, I would expect a TargetInvocationException, which is probably why the author didn't have any luck catching a NotImplementedException... In the case of a read only field, you would probably need to handle a FieldAccessException, though I've never tried this so I can't be sure.

    And there's more .... I'm not sure about the decision to include BindingFlags.DeclaredOnly but just FYI, this means that the code will only copy properties that are declared on the specific type. It will not copy properties that were inherited from further up in an inheritance chain. For instance, let's say FixedJoint inherits from Joint like this:

    Code (csharp):
    1. class Joint : Component
    2. {
    3.   public int Springiness { get; set; }
    4. }
    5.  
    6. class FixedJoint : Joint
    7. {
    8.   public Vector3 FixedAtPoint { get; set; }
    9. }
    It's clear that FixedJoint should have two properties, Springiness and FixedAtPoint. However, if you apply this code to FixedJoint, it will only copy the FixedAtPoint property. The Springiness property will not be copied.

    Also, @BoredMormon touched on this, the reflection code is making a shallow copy of these properties and fields. For value types, like Springiness in my example, this is not an issue because there is no such thing as a shallow copy of value types (which is what int is). However, for reference types (classes mostly), you will end up with two Components referring to the same instance. In my example, this would be pretty wacky because both FixedJoints would be "FixedAt" the same "Point".

    You'll have to excuse my very bad example here, I'm not familiar with Joints, Skeletons, Modeling, etc...
    Let's just say that we use the FixedAtPoint property to control a model's hand location in local coordinates and that by moving the FixedAtPoint property, we can make the model wave at us. If we used this code to copy the joints such that we can create a new model, the two models would have unique FixedJoints however both FixedJoints would refer to the same FixedAtPoint. That means if we made the original model wave by adjusting the FixedAtPoint property, we would inadvertently make the second model also wave. Weird.

    Remember I said earlier that reflection is kind of like teaching a computer to do what programmers do in their heads. This is hard for exactly the reasons I pointed out. A programmer with some experience might be able to anticipate these problems but it is a lot of work to teach the computer how to anticipate these problems. So instead of using reflection to get all the properties and fields on a class and blindly copying them over lets try providing a map, which will be used by our code to map one components properties to another component.

    Code (csharp):
    1. public static T AddClonedComponent<T>(this GameObject gameObject, T componentToClone, ICloningMap<T> cloningInstructions) where T : Component
    2. {
    3.     var newComponent = gameObject.AddComponent<T>();
    4.     cloningInstructions.MapProperties(componentToClone, newComponent);
    5.     cloningInstructions.MapFields(componentToClone, newComponent);
    6.     return newComponent;
    7. }
    8.  
    9. interface CloningMap<T>
    10. {
    11.   void MapProperties(T from, T to);
    12.   void MapFields(T from, T to);
    13. }
    Then, to use this we would need to define the CloningMap's ourselves. This has the advantage that we can very specifically deal with issues like deep copying or read only properties. unfortunately, it is more work, because we need to create a map for each type of Component we expect to find and explicitly copy over each property and field that we are interested in.

    Code (csharp):
    1. class JointCloningMap : CloningMap<Joint>
    2. {
    3.   public void MapProperties(Joint from, Joint to)
    4.   {
    5.     to.Springiness = from.Springiness;
    6.   }
    7.  
    8.   public void MapFields(Joint from, Joint to)
    9.   {
    10.   }
    11. }
    12.  
    13. class FixedJointCloningMap : CloningMap<FixedJoint>
    14. {
    15.   public void MapProperties(Joint from, Joint to)
    16.   {
    17.     to.Springiness = from.Springiness;
    18.     to.FixedAtPoint = new Vector3(from.FixedAtPoint.x, from.FixedAtPoint.y, from.FixedAtPoint.z);
    19.   }
    20.  
    21.   public void MapFields(Joint from, Joint to)
    22.   {
    23.   }
    24. }
    Unfortunately, we create a new problem for ourselves: how do we tell the code that a Joint needs a JointCloningMap and not a FixedJointCloningMap? For this, we could use type checking or we could use a registration process..

    Anyways, I digress.. and I'm all out of time. Hopefully it's helpful..
     
    twobob and fogsight like this.
  17. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    Minor quibble: Vector3 is a struct rather than a class, and would be fully copied just like an int would. Conceptually you're correct, you just picked the wrong type to use as an example. ;)
     
    Nanako likes this.
  18. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
    Oops! thanks for pointing it out. I haven't really gotten into Unity yet..
    Just collecting concepts from other's answers and adding where there is crossover into regular C#.
     
  19. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    eisenpony i love you. i'm only halfway through reading this. :eek:
     
  20. unwitty

    unwitty

    Joined:
    Jan 18, 2015
    Posts:
    31
    I'm having trouble with this script as well.

    VSB is telling me that there's a problem with `this` in `public static T GetCopyOf<T>(this Component comp, T other) where T : Component`

    The error message:

    Assets/Scripts/Test.cs(21,21): error CS1106: `Test.GetCopyOf<T>(this UnityEngine.Component, T)': Extension methods must be defined in a non-generic static class
     
  21. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    The problem is on an earlier line. If you are going to use this to do an extension, you must make the class static.
     
  22. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    Extension methods must be defined in non-generic static classes.

    Which means that the method needs to go in a class looking like this:

    Code (csharp):
    1. public static class SomeClass {
    2.  
    3.     public static void MyExtension(this UnityEngine.Component component) {
    4.         ...
    5.     }
    6. }
    EDIT: SNIPED!
     
    twobob and Kiwasi like this.
  23. unwitty

    unwitty

    Joined:
    Jan 18, 2015
    Posts:
    31
    Thanks for the help. I've made the changes and the code compiles. I'm able to copy a component (Configurable Joint) but some of the component values do not have the right values. For example:

    - Connected Body gameobject is incorrectly set to none
    - Anchor Vector3 is different
    - Connected Anchor Vector3 is different

    Here's how I'm using it:

    Code (CSharp):
    1. joint = GetComponentInChildren<ConfigurableJoint>();
    2. joint.gameObject.AddComponent<ConfigurableJoint>(joint);
    EDIT: Auto Configure Connected Anchor is also set to true in the copied version but it's false in the original. Could this be causing the issue? Why didn't this boolean value get copied?
     
    Last edited: Mar 12, 2017