Designing Enemies Using Abstract Classes

You might have different types of an objects in the game such as different enemy types, different food types etc. If you design these similar but different objects one by one, you will realize that they will be sharing many parts with each other.

Consider Diablo series; there are many types of enemies with different attack variations. They may look or attack differently; but all of them share basic components such as health, moving speed or to be triggerred to attack within a range.

Diablo III

Does it really make sense to redefine the same components again and again? Imagine a game with tens or hundreds of enemy type — You got the point: Defining each one by one would be so inefficient.

Here comes the Abstract Classes to help. As can be guessed from its name, abstract class is a class that acts us a roadmap for the classes that are going to inherit from — its just a template. A subclass (the class that inherits from an abstract class) takes the abstract class as a template and finishes the class within the subclass.

In the Dungeon Escape came, we have 3 types of enemies namely Moss Giant, Skeleton and Spider. These enemies look different, attack different but all of them are still enemies. For the basic information they are going to share, it is efficient to create an enemy abstract class that each of these will inherit from.

As can be seen above, all of these 3 types of enemies are going to share the variables of speed, health, gems and Attack — of course with different values. By defining an enemy abstract class with speed, health and gem variables and an Attack method, we can eliminate duplicate coding in our game.

Rather than duplicating the same code many times, we only define it once at the abstract class.

So in this game, all of the different type of enemies will inherit from the Enemy class and overwrite the values of their own.

Using Abstract Classes

An abstract class can be defined by using “abstract” keyword just before class.

public abstract class Enemy : MonoBehaviour

By creating an abstract class, we actually do not create an instance of it. As stated above, it is just a template. The instances are created by the subclasses that inherit from the abstract class.

While defining a subclass, we need to state that it is going to inherit from the abstract class, not from MonoBehaviour. Not using MonoBehaviour does not cause any problem to use the methods within Monobehaviour, as the abstract class already inherits from MonoBehaviour.

public class MossGiant : Enemy

As you already know, we do not want to declare the variables as mush as possible if they are not needed to be reached by other scripts. In the case of abstract classes, we only want the variables be changeable by the scripts that inherits from the abstract class (of course, since we are not going to have an instance of the abstract class, this is actually for enabling the subclasses to have their unique values). The attribute “protected” is used for this purpose. By defining the variables with protected attribute, we give permission to the classes that inherit from the abstract class change the value, whereas another script that does not inherit the abstract class does not access.

To define a method within the abstract class, we use the abstract keyword again.

public abstract void Update();

Abstract methods needs to be defined empty — with no implementation. Defining such method simply tells the subclass that it shall also include the method. In other words, we simply state to the subclass that it is not optional to include this method — it is a must! The subclass will give out an error if you do not include an abstract method from the inherited abstract class.

Error as a result of Mossgiant does not include the abstract Update method

To implement an abstract Update method within a subclass, we need to define it as :

public override void Update()

Additionally, some of the subclasses might share a method, whereas some of them might have unique characteristics within that method. Virtual methods can be used for this purpose. Virtual methods are not mandatory for the subclasses, they will not give out an error if you decide not to implement it within a subclass. Just like the abstract methods, we can override an inherited virtual method by using override keyword :

public override void Attack()

In the inherited virtual method, using base.(methodname) will implement the code same as the abstract class. It is crucial to note that if we do not provide the base implementation, the subclass will not implement the base method provided in the abstract class. By default, Unity automatically adds the base line once we call it from the subclass:

Briefly, abstract classes are really handy to eliminate duplicate coding. For instance, in the Dungeon Escape game, all of the 3 enemy types patrol along a route of their own (we manipulate the routes from the inspector individually). Rather than duplicating the same code for each of them, we can implement the code only one time within the abstract class.

Here is the coding for the Enemy abstract class:

Even if we do not provide any code at all in the individual Moss Giant or Spider classes, they will still be moving within their route, as they inherit from the Enemy.

As I have stated above, it is easy to personalize each of the individual enemy subclass within their own script.

Unity Developer