[SOLVED] C#: Adding a component dynamically

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?

1 Like

the effect class you want to add…

1 Like

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?

1 Like

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.

1 Like

Store it where? And sure…

Type myType = effectType.GetType();

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.

well according to you, it does… :face_with_spiral_eyes:

Yes, it does indeed. So the following code works:

    public void AssignTroop(string troopName)
    {
        BaseClasses.Troop troopToAssign = cardsList.cardsDict[troopName];
        this.gameObject.AddComponent(EffectsList.CustomEffect);
    }

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:

    public void AssignTroop(string troopName)
    {
        BaseClasses.Troop troopToAssign = cardsList.cardsDict[troopName];
        if (troopToAssign.effectType != null)
        {
            this.gameObject.AddComponent(troopToAssign.effectType);
        }
    }

and have the actual type of my effect listed in my Troop instance as a variable. Is this possible?

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:

  public class Effect : MonoBehaviour {
     // Normal contents
     // ...

     protected delegate Effect FactoryMethod ( GameObject go );
     private static Dictionary<string,FactoryMethod> _effectMap = new Dictionary<string,FactoryMethod>();

     protected static void AddMapping ( string type, FactoryMethod fact ) {
       _effectMap.Add(type, fact);
     }

     public static Effect AddComponent ( GameObject go, string type ) {
       return _effectMap[type](go);
     }
   }

   public class ExplosionEffect : Effect {
     // Normal contents
     // ...

     static ExplosionEffect () {
       AddMapping("ExplosionEffect", go => go.AddComponent<ExplosionEffect>());
     }
   }

   public class Troop : MonoBehaviour {
     public string effectType;

     public void Start () {
       Effect eff = Effect.AddComponent(gameObject, effectType);
     }
   }

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.

I thought there would be a more elegant solution than re-implementing the AddComponent(string t) but thanks a lot for this solution Errorsatz!

Why do you need a valid instance to get the type of a class?

public Type effectType = typeof(MyEffectClass);

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.

@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?

It should be, yes.

Thank you, I marked the thread as solved.