In my new game, I have a GameManager that instantiates enemies. I'd like my enemy prefabs to all share a common IEnemy interface. How do I make my GameManager use the interface instead of the concrete Enemy type?

My starting code is this:

public class Enemy : MonoBehaviour
{
  ...
}

class GameManager {
  public Enemy enemyPrefab;

  private void Start()
  {
    InstantiateEnemy();
  }

  private void InstantiateEnemy() {
    var enemy = Instantiate(enemyPrefab);
    enemy.OnStart += (object sender, System.EventArgs e) => (sender as Enemy).AddHp(enemyHealth);
    enemy.OnDeath += (object sender, System.EventArgs e) => StartCoroutine(InstantiateEnemy());
  }
}

I would like to introduce a new type of enemy, say UndeadEnemy, so I can't explicitly refer to Enemy in GameManager anymore. I want to introduce this IEnemy interface:

using System;

public interface IEnemy
{
    public event EventHandler OnStart;
    public event EventHandler OnDeath;
    public void AddHp(int hitPoints);
}

And make my Enemy implement it:

public class Enemy : MonoBehaviour, IEnemy
{
  ...
}

Finally, the hard part is to make GameManager use it.

  • If I change the prefab type to IEnemy (public IEnemy enemyPrefab;), Instantiate won't work anymore.
  • If I make the prefab a GameObject, and Instantiate<IEnemy>, it won't compile either:
  • Casting the GameObject to an IEnemy doesn't either:
CS0039: Cannot convert type 'UnityEngine.GameObject' to 'IEnemy' via a reference conversion, boxing conversion, unboxing conversion, wrapping conversion, or null type conversion
  • Nor does checking for an interface after it has been instantiated:
CS0184: The given expression is never of the provided ('IEnemy') type
  • GetComponent<IEnemy> (var enemy = enemyGameObject.GetComponent<IEnemy>();) seems to compile, but on runtime, it actually resolves to null, and it throws an exception.
  • MonoBehavior and GameObject are a concrete classes, so you can't make IEnemy implement them.

After all this failed, I googled for an answer and found this: https://www.cjonesdev.com/blog/unity-getcomponentlttgt-and-interfaces.

The author of the post recommends using Abstract classes instead. But I don't like it since it will force me to create an Abstract class for every Interface that I make, and every implementation will have to inherit that Abstract class. I really want to keep interface and implementation separate.

At the bottom fo the post, a certain Paul White shared his solution:

IWeapon weaponComponent = (IWeapon)GetComponent(typeof(IWeapon));

And indeed, it compiles and works as you'd expect at runtime. You can cast with (IWeapon) component or component as IWeapon.

In the end, the final version of my GameManager is this:

class GameManager {
  public GameObject enemyPrefab;

  private void Start()
  {
    InstantiateEnemy();
  }

  private void InstantiateEnemy() {
    var enemyGameObject = Instantiate(enemyPrefab);
    var enemy = enemyGameObject.GetComponent(typeof(IEnemy)) as IEnemy;
    enemy.OnStart += (object sender, System.EventArgs e) => (sender as IEnemy).AddHp(enemyHealth);
    enemy.OnDeath += (object sender, System.EventArgs e) => StartCoroutine(InstantiateEnemy(2));
  }
}

One last thing. One advantage I could see in using Abstract classes is that I can force the prefab to be of the type that I want. Right now, it's set to a GameObject, so one could accidentally drag soemthing else than an IEnemy.