2D Mobile: Designing Enemies Using Abstract Classes

Today I went ahead with the idea of creating an abstract Enemy class to share common functionality for derived classes.

Each enemy will share:

  • Health
  • Speed
  • Transform points from A to B
  • Attack method
  • Move method
  • and possibly more…

It wouldn’t make sense to code all of this for each enemy, especially when it is all the same! What if I had 50+ enemies that have similar functionality? I would be creating duplicate code that could be shared with all in one abstract base class.

Here’s my example of an Enemy base class that shares all common functionality with its child classes.

using UnityEngine;public abstract class Enemy : MonoBehaviour
{
[SerializeField] protected int health;
[SerializeField] protected float speed;
[SerializeField] protected int gems;
[SerializeField] protected Transform pointA, pointB;
protected Animator enemyAnim;
protected SpriteRenderer enemySprite;
protected Vector3 currentTarget;
public virtual void Awake()
{
InitComponents();
}
public virtual void Start()
{
InitData();
}
public virtual void Update()
{
if (enemyAnim.GetCurrentAnimatorStateInfo(0).IsName("Idle")) return;
Move();
}
//Initialize All References to Components
public virtual void InitComponents()
{
enemyAnim = GetComponentInChildren<Animator>();
enemySprite = GetComponentInChildren<SpriteRenderer>();
}
//Initialize Data from Child Classes
public virtual void InitData()
{
//base
}
//Move all enemies from Point A to B.
public virtual void Move()
{
if (currentTarget == pointA.position)
enemySprite.flipX = true;
else if (currentTarget == pointB.position)
enemySprite.flipX = false;
if (transform.position == pointA.position)
{
currentTarget = pointB.position;
enemyAnim.SetTrigger("idle");
}
else if (transform.position == pointB.position)
{
currentTarget = pointA.position;
enemyAnim.SetTrigger("idle");
}
transform.position = Vector3.MoveTowards(transform.position, currentTarget, speed * Time.deltaTime);
}
}

Here’s an example of a child class inheriting from the abstract Enemy base class:

public class Skeleton : Enemy
{
public override void InitData()
{
base.InitData();
speed = 0.6f;
}
}

Not much to it besides adjusting the speed by overriding the InitData function, but keeping the base implementation intact. The enemy base class is handling the movement from point A to B. And when I start working on the Attack for each enemy, I will have a dedicated abstract method for the Attack since each enemy attack in a unique way.

In the inspector, my skeleton game object receives the available data from the Enemy class as its own.

And if I need to extend a particular child class, I can add more functionality within overridable methods. If I want to completely not use the behavior from a particular base method, I can override it and code in different behavior.

My Skeleton game object runs perfectly by inheriting from the abstract Enemy base class!

So instead, create an abstract base class and keep in mind the common functionalities each enemy will share. It will save you so much time and frustration, and also it will keep your code clean and simple.

Use abstract classes when classes are closely related.

Gabriel

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store