Interfaces: Calling the Same Methods from Similar Classes
Objective: Set up a system allowing different projectiles to deal different amounts of damage to enemies.
When we started working on the new enemy for Galaxy Shooter, I mentioned that this enemy should be destroyed by either three lasers or one missile. However, right now it takes three of each.
That’s because missiles, just like lasers, only deal one damage at a time. Both objects are tagged as a “Projectile” and they cause enemies to take a single damage. This is a problem that’s very easy to solve by brute force, but deceptively difficult to solve the right way.
We could fix this by creating a new tag for the missile, and check for the tag and deal three damage instead by calling OnTakeDamage(3). But if were to add even more projectile types later, then we’d have to remember to constantly add new tags for every new projectile. What a pain that would be! Also, the EnemyLives class would then be in charge of deciding how much damage it would take from each projectile, which sounds completely backwards.
The projectiles themselves should have some damage amount stored in it’s class, and we should be able to get access to that by calling a public method. Now when an enemy collides with this projectile, we can just get the script attached to it and access this damage value. Unfortunately, there’s still one big problem with this: Lasers and Missiles are different scripts, and we have to way to tell which script to get from the gameobject because they are both tagged “Projectile”.
We could try each type of script and test if each exists by using a bunch of if statements, but this is horribly inefficient and we run into the same problem of having to create more and more if statements for every new type of projectile. Yuck!
What we need is for all kinds of projectiles to share the same class, called Projectile, that has a public GetDamage method. Then instead of trying to find a Laser or Missile script, we can just get the Projectile script and call GetDamage. Time to use Inheritance, right…?
The last remaining issue is that using basic class inheritance as a solution can only be done once, because each class can only ever inherit from a single other class. If we later want a new weapon like a Bomb, that has both Projectile and Explosive features, we can’t inherit from both the normal way.
This is where an Interface is very useful. It’s a special kind of base class that cannot have fields and only contains abstract methods that must be implement in classes that inherit from it. What’s great is that classes can inherit from as many interfaces as they want, so we’ll never have any problems later if we want a single class to have features from multiple interfaces.
Now all we need to do is have the Laser, Missile, and any future kinds of projectiles to inherit from this IProjectile interface, and tag them as Projectile in the inspector. The EnemyLives.OnTriggerEnter2D method will always be able to find a component script of that interface type, and call GetDamage to find out how much damage the enemy should take.
Now we have weapons that do different amounts of damage, and we can rest easy knowing that we’ve set it up the right way, and made our work much easier in the future.