A Base Enemy Script
Part of the Game Architecture in Unity section of Coddy's Unity journey. Lesson 2 of 64.
In C#, a class can derive from another: class Ghost : Enemy gets every field and method of Enemy and adds its own. A method the base marks virtual may be replaced in the subclass with override, and base.Describe() runs the base's version from inside the new one. In plain C#:
class Enemy
{
protected int hp = 3;
public virtual string Describe()
{
return "Enemy with " + hp + " hp";
}
}
class Ghost : Enemy
{
public override string Describe()
{
return base.Describe() + ", see-through";
}
}A Ghost's Describe() returns Enemy with 3 hp, see-through. protected means Enemy and its subclasses can use hp, and other classes cannot.
In Unity, enemies share most of their code: hit points, a speed, a way to move. That code lives in a base class that derives from MonoBehaviour, and each kind of enemy derives from it. This Enemy sets its hit points in Start and walks where it faces in Update, and a Ghost changes the start to move twice as fast:
public class Enemy : MonoBehaviour
{
public int maxHp = 3;
public float speed = 2f;
protected int hp;
protected virtual void Start()
{
hp = maxHp;
}
protected virtual void Update()
{
transform.Translate(Vector3.forward * speed * Time.deltaTime);
}
}public class Ghost : Enemy
{
protected override void Start()
{
base.Start();
speed *= 2f;
}
}Ghost inherits the fields, so the Inspector shows maxHp and speed on a Ghost, and it inherits Update, so it walks without an Update of its own. hp is protected: Enemy and its subclasses can use it, other scripts cannot.
Unity finds Start and Update by name, whatever their access, and calls the one in the most derived class. If Enemy's Start were a private void Start() and Ghost declared its own, Unity would call only Ghost's: Enemy's would never run, and hp would stay 0 with no error. Declaring the base's messages protected virtual makes this plain C#: the subclass writes override, and base.Start() runs the base's part at the point where you call it. Leave the call out and the base's part is skipped, which is how a subclass replaces a step instead of adding to it.
Challenge
EasyMake Golem and Wasp run Enemy's part before their own.
- In
Enemy, writeprotected virtualin front ofvoid Start()andvoid Update(). - In
Golem, make itprotected override void Start()withbase.Start();as its first line. - In
Wasp, make itprotected override void Update()withbase.Update();as its first line.
Press Play: the golem marches with full hp, and the wasp zigzags as it marches. Then Check.
Try it yourself
using UnityEngine;
// What every monster shares.
public class Enemy : MonoBehaviour
{
public int maxHp = 4;
public float speed = 1.5f;
protected int hp;
protected Vector3 spawnPoint;
void Start()
{
hp = maxHp;
spawnPoint = transform.position;
}
void Update()
{
// march on the castle, down the z axis
transform.position += Vector3.back * speed * Time.deltaTime;
}
}
All lessons in Game Architecture in Unity
1Components, Not Hierarchies
Small ComponentsA Base Enemy ScriptAbstract ComponentsComponents Working TogetherRecap: Modular TurretRecap: Twin Cannon4Serialized Data
Serializable ClassesLists in the InspectorEnums in the InspectorRecap: Loot TableRecap: Roll the Loot2Interfaces in Unity
IDamageableInterfaces and TryGetComponentGetComponentsRecap: Interact KeyRecap: Nearest in ReachPractice on your own: Unity playground