Using reflection to instantiate an enemy type

Yeah, it’s me again, this time with a slightly more advanced question. I have an enemy class from which a bunch of enemy types come from. See code:

public class Enemy<T> where T : Enemy {
    public GameObject gameObject;
    public T ScriptComponent;

    public Enemy(string name) {
        gameObject = new GameObject (name);
        ScriptComponent = gameObject.AddComponent<T> ();
    }
}

public class Jackass : Enemy {
    public override void Initialize(float healthpoints_, int healthNotches_, float speed_, float acceleration_, Quaternion rotation_, Vector3 position_){
    healthPoints = healthpoints_;
    healthNotches = healthNotches_;
    speed = speed_;
    transform.rotation = rotation_;
    transform.position = position_;
    }

    protected override void MovementPattern () {

    }
}

public class PistolJackass : Enemy {
    public override void Initialize(float healthpoints_, int healthNotches_, float speed_, float acceleration_, Quaternion rotation_, Vector3 position_){
    healthPoints = healthpoints_;
    healthNotches = healthNotches_;
    speed = speed_;
    transform.rotation = rotation_;
    transform.position = position_;
    }

    protected override void MovementPattern () {

    }
}

It continues on like this for 13 different enemies so far.

I also have my spawner script, which uses reflection to get a list of all the enemies that can be spawned and puts them nicely in a popup box.

public class EnemyFactory : MonoBehaviour {

    [HideInInspector]
    public int selectedEnemy;

    public float healthPoints;
    public int healthNotches;
    public float speed;
    public float acceleration;

    void Awake () {
        var type = typeof(Enemy);

        var listOfDerivedClasses = Assembly.GetExecutingAssembly ().GetTypes ().Where (x => x.IsSubclassOf (typeof(Enemy))).ToList ();

        for (int i = 0; i < listOfDerivedClasses.Count; i++) {
//            Debug.Log (listOfDerivedClasses [i].Name.ToString ());
        }

        Debug.Log(selectedEnemy);

        Enemy<Jackass> placedEnemy = new Enemy<Jackass> (listOfDerivedClasses[selectedEnemy].Name.ToString());
        placedEnemy.ScriptComponent.Initialize(
            healthNotches_: healthNotches,
            healthpoints_: healthPoints,
            speed_: speed,
            acceleration_: acceleration,
            rotation_: Quaternion.identity,
            position_: transform.position);

    }

}

This works so long as I only want to spawn the Jackass enemy variant, but I have to hard code everything into enums if I want to have a dropdown menu that encompasses all enemy types. This is less than ideal because I’d like to have the ability to easily remove/add new enemies as time goes on.

I’ve looked into reflection for this, as it seems to be the way to go. I don’t have to worry too much about speed since this is just to spawn in enemies as the level launches. I won’t be calling this every frame or anything ridiculous like that.

So far I’ve come across loads of variants of the following code:

MethodInfo method = typeof(Sample).GetMethod("GenericMethod");
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

But for the life of me I can’t get it to work without getting some sort of null reference exception. Does anyone here know what I should be doing?

I have to say that having a generic Enemy and a concrete Enemy that have no relation to each other is all sorts of confusing.

You can create an instance of a generic typed class by using MakeGenericType, then using System.Activator.CreateInstance to spawn an instance of the type. See this stackoverflow answer for details (How to dynamically create generic C# object using reflection? - Stack Overflow)

You’re dead-on correct about the generic and the concrete Enemy having the same name. I’ve since updated the concrete Enemy class to be renamed to EnemyBase, which has improved readability a lot.

On to the less good news, however. I’m afraid I need a bit more of a direct hand because no matter how much I try to apply the numerous SO answers I’ve searched through, including yours, I simply can’t get it to work. I’m afraid this is what happens when a humanities major takes up gamedev. I don’t suppose you could dumb it down a little for me?

Don’t waste your time with that, as you’d only be able to create instances that are not properly wired up due to the fact that your Enemy base class is a MonoBehaviour.

I can’t quite follow the reason why you’ve chosen this way, but you could drop the generic approach and use one of the other overloads of AddComponent to model such dynamic behaviour.

That way, you could get all the subclasses of the enemies (which - just btw - should also be non-abstract, add that to your query) and either store the types names in a list (as it is done atm) and use AddComponent(string) or store the System.Type and use AddComponent(Type).

The drawback is that you lose the generic “ScriptComponent” field, which would then be of type “Enemy” (or “EnemyBase”, since you’ve changed it). As long as you don’t need special methods of a concrete type you’d be fine though.

So I’m totally barking up the wrong tree here? I mostly came to this conclusion because multiple google attempts kept pointing me at reflection docs and SO answers.

Probably. You should not use the Activator to create instances (just like you wouldn’t use a constructor to create a new MonoBehaviour), but rather an overload of the AddComponent method.

1 Like

Figures, and after I already put like two days into getting this to work. Still, you pointed me in the right direction and I have a (mostly) functional solution now, so thanks!

Glad to hear that.
Why mostly? What’s missing?

Mostly little stuff I’ll have to fiddle with, like properly initialising the enemies when they spawn, since that used to be handled by the generic. I can figure it out on my own most likely though.

If you find yourself getting stuck again, don’t hesitate to ask.

Bad news, I’m stuck again.

I had an idea to have the default values of an enemy (hitpoints, speed, etc.) displayed in my custom inspector when I select an enemy from the popup list. However, my initial plan of just using

listOfDerivedClasses [choiceIndex].UnderlyingSystemType.GetField("_healthPoints")

doesn’t seem to work. It seems that to use GetValue, I need to assign an object to the lookup, which I can’t figure out how to do programmatically so that the dropdown still works.

Can you point me in the right direction, please?

edit: Got it!
```csharp
**object instance = Activator.CreateInstance (listOfDerivedClasses [choiceIndex].UnderlyingSystemType);

    Debug.Log (listOfDerivedClasses [choiceIndex].UnderlyingSystemType.GetField("_healthPoints").GetValue(instance));**

```

It works! Somehow! I just kinda browsed a billion google search results until I found something vaguely similar on SO and adapted it to my needs. Well, it kinda works. Unity gets all my values and displays them just fine, it just vomits up a billion of the same warning in the console.

“You are trying to create a MonoBehaviour using the ‘new’ keyword. This is not allowed. MonoBehaviours can only be added using AddComponent(). Alternatively, your script can inherit from ScriptableObject or no base class at all”

Still, I can live with that.

That’s what I was talking about. You aren’t supposed to instantiate MonoBehaviours neither with a constructor nor via reflection.

Try to access the transform or the gameObject of the object that you’ve created. It should throw expections as it’s probably not properly initialized on the C++ side.

Well, in this case, there is no actual gameobject for the object. It’s mostly entirely for choosing which class enemy is going to be spawned.

Truth be told, I’m not super bothered by this as the code is never going to be executed at runtime, so the side effects are mostly a slightly cluttered warning section in my debug log.

What’s the point to derive from MonoBehaviour then? If you removed the inheritence, you wouldn’t have these warnings at all.
If that’s rather some kind of editor tool, you’d be better off writing an editor extension anyway :wink:

As much as I love reflection, I’m puzzled as to why it’s your tool of choice here. In many cases if the answer is Reflection, then the question is wrong.

What’s wrong with a regular prefab? Perhaps with some polymorphism thrown in for good measure?

1 Like

Suggested something similar with a rough idea how to use that with his way of doing things. Guess he’s got his reasons to not follow along.

1 Like