Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

[SOLVED] C#: Adding a component dynamically

Discussion in 'Scripting' started by Bibibis, Jun 9, 2016.

  1. Bibibis

    Bibibis

    Joined:
    Jul 28, 2015
    Posts:
    11
    Hello,

    I have a class Troop, which contains the informations related to a specific troop. I have a troop prefab, which is the base of my troops with no additional features. The class Troop contains an effectType variable, which is the type of the effect this troops has.

    Now some of my troops can have an effect, for example an area of effect buff, a periodical summon, and so on. All of those effects are defined as classes, and all of them inherit from a base abstract class Effect, which in turn inherits from MonoBehaviour.

    At runtime, when instantiating a new troop, I'd like to attach to it the correct effect as a script using gameObject.AddComponent(effectType).

    What should the type of my effectType variable be?
     
    Last edited: Jun 9, 2016
    honor0102 likes this.
  2. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    the effect class you want to add...
     
    honor0102 likes this.
  3. Bibibis

    Bibibis

    Joined:
    Jul 28, 2015
    Posts:
    11
    That should be the content of the variable, not the type, right? Is it not possible to declare a variable of type "Type" and store a type in there?
     
    honor0102 likes this.
  4. Errorsatz

    Errorsatz

    Joined:
    Aug 8, 2012
    Posts:
    555
    Assuming you're using the non-generic version of AddComponent, ie:
    Effect eff = gameObject.AddComponent(effectType) as Effect;

    Then effectType is a string, which would contain the name of the effect class desired.
     
    honor0102 likes this.
  5. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Store it where? And sure...

    Type myType = effectType.GetType();
     
  6. Bibibis

    Bibibis

    Joined:
    Jul 28, 2015
    Posts:
    11
    The problem is that to use effectType.GetType() I need a valid instance of the class I want in effectType, and I can't create an instance at runtime because my Effect class inherits from MonoBehaviour. I should note all of my Troop variables are set in Awake().

    This is the root of my problem: to be able to use AddComponent(customType), customType needs to inherit from MonoBehaviour, and to use effectType.GetType() in order to find what my customType is effectType cannot inherit from MonoBehaviour because it's instantiated at runtime.

    @Errorsatz That's deprecated in Unity 5.
     
    Last edited: Jun 9, 2016
  7. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    well according to you, it does... :confused:
     
  8. Bibibis

    Bibibis

    Joined:
    Jul 28, 2015
    Posts:
    11
    Yes, it does indeed. So the following code works:

    Code (CSharp):
    1.     public void AssignTroop(string troopName)
    2.     {
    3.         BaseClasses.Troop troopToAssign = cardsList.cardsDict[troopName];
    4.         this.gameObject.AddComponent(EffectsList.CustomEffect);
    5.     }
    The problem is that I don't want EffectsList.CustomEffect everytime: I want a different effect based on which troop I'm currently dealing with. I would like to use the following code:

    Code (CSharp):
    1.     public void AssignTroop(string troopName)
    2.     {
    3.         BaseClasses.Troop troopToAssign = cardsList.cardsDict[troopName];
    4.         if (troopToAssign.effectType != null)
    5.         {
    6.             this.gameObject.AddComponent(troopToAssign.effectType);
    7.         }
    8.     }
    and have the actual type of my effect listed in my Troop instance as a variable. Is this possible?
     
  9. Errorsatz

    Errorsatz

    Joined:
    Aug 8, 2012
    Posts:
    555
    Hadn't noticed that was deprecated; I generally use the the other form. You can still do the mapping manually, although the way AddComponent works makes it a little awkward.

    For example:
    Code (csharp):
    1.  
    2.   public class Effect : MonoBehaviour {
    3.      // Normal contents
    4.      // ...
    5.  
    6.      protected delegate Effect FactoryMethod ( GameObject go );
    7.      private static Dictionary<string,FactoryMethod> _effectMap = new Dictionary<string,FactoryMethod>();
    8.  
    9.      protected static void AddMapping ( string type, FactoryMethod fact ) {
    10.        _effectMap.Add(type, fact);
    11.      }
    12.  
    13.      public static Effect AddComponent ( GameObject go, string type ) {
    14.        return _effectMap[type](go);
    15.      }
    16.    }
    17.  
    18.    public class ExplosionEffect : Effect {
    19.      // Normal contents
    20.      // ...
    21.  
    22.      static ExplosionEffect () {
    23.        AddMapping("ExplosionEffect", go => go.AddComponent<ExplosionEffect>());
    24.      }
    25.    }
    26.  
    27.    public class Troop : MonoBehaviour {
    28.      public string effectType;
    29.  
    30.      public void Start () {
    31.        Effect eff = Effect.AddComponent(gameObject, effectType);
    32.      }
    33.    }
    The advantage to a factory approach like this is that you can add other behavior besides just adding the component. For instance, if a particular effect required additional setup.
     
    Last edited: Jun 9, 2016
  10. Bibibis

    Bibibis

    Joined:
    Jul 28, 2015
    Posts:
    11
    I thought there would be a more elegant solution than re-implementing the AddComponent(string t) but thanks a lot for this solution Errorsatz!
     
  11. jimroberts

    jimroberts

    Joined:
    Sep 4, 2014
    Posts:
    560
    Why do you need a valid instance to get the type of a class?
    Code (CSharp):
    1. public Type effectType = typeof(MyEffectClass);
    2.  
    3. gameObject.AddComponent(effectType);
    P.S. I don't think there is EVER an instance where using a string with AddComponent or GetComponent is a valid solution in C# when you have typeof/GetType available to you, with the exception of serialization of types.
     
    Last edited: Jun 10, 2016
  12. Bibibis

    Bibibis

    Joined:
    Jul 28, 2015
    Posts:
    11
    @jimroberts No, actually you're absolutely right, I can use that. I didn't realize using typeof() on something made it possible to store in a variable of type System.Type... Is System.Type usable on all platforms?
     
  13. jimroberts

    jimroberts

    Joined:
    Sep 4, 2014
    Posts:
    560
    It should be, yes.
     
  14. Bibibis

    Bibibis

    Joined:
    Jul 28, 2015
    Posts:
    11
    Thank you, I marked the thread as solved.