A common problem I see in novice Unity game programmers is that they often create too much unnecessary, duplicate code. Take, for example, the programmer who creates a simple 2D platformer with a player character and a generic enemy character to be duplicated across the level. What often happens is that two separate scripts will be created — one for the player character, and one for a generic enemy character. Each individual script will define its own behaviours for things such as movement, jumping, dealing damage and receiving damage, and most of the code between these two scripts is similar because the character types have so much in common.
This style of programming is not effective, for two simple reasons:
- There is a lot of duplicated code between the scripts because most of the defined behaviours are similar. As the project grows and more types of characters are created (hence more scripts), the amount of duplicated code will increase further.
- More importantly, because every script has the same functionalities coded slightly differently from each other, the project will become exponentially more difficult to manage as it grows.
So what’s the solution?
Make use of object inheritance as much as possible when scripting. That is, instead of building each of our scripts separately, we build them on top of our existing scripts as much as possible. This means that, using the above example of a player character and an enemy character, we organise our scripts like this:
Now, instead of being completely unrelated, the player and enemy character scripts both inherit the behaviours of a parent Character
class, which defines behaviours common to both scripts. This means that the individual scripts for the PlayerCharacter
and EnemyCharacter
will be much shorter. It also means that if you create more character types in the game, you can base them off the same parent Character
class and go straight into scripting behaviours unique to the character type.
Basics of inheritance
Inheritance is not a feature that is unique to Unity or game programming. It is found in many scripting languages in a variety of forms, and is an especially useful tool in game programming because — when properly done — it helps simplify and provide structure to code.
To make use of inheritance in your Unity script, simply replace MonoBehaviour in your class definition with the class that you want to inherit from.
public class PlayerCharacter :MonoBehaviourCharacter
This will cause PlayerCharacter
to inherit all the properties (i.e. variables) and methods (i.e. functions) belonging to the Character
class. In simpler words, it means that the PlayerCharacter
class is now able to do whatever the Character
class (defined below) can do.
public class Character : MonoBehaviour {
public int health = 100;
protected int maxHealth = 100;
private bool _isAlive = true;
public Rigidbody2D physics;
public void Start() {
physics = GetComponent<Rigidbody2D>();
}
public virtual void TakeDamage(int amount) {
health -= amount;
// Manage the other properties related to health.
if(health < 0) {
health = 0;
_isAlive = false;
} else if(health > maxHealth) {
health = maxHealth;
}
}
public abstract void Attack();
}
So, even if your PlayerCharacter
script is empty, you are still able to call — for example — the TakeDamage()
method, or check its health
property. It’s like all of the code in Character
is also in the PlayerCharacter
script too.
Overriding inherited methods
Let’s say that for your PlayerCharacter
script, you want to have a property called damageResistance
which reduces the amount of damage your character takes. This means that you will need to redefine how the TakeDamage() function works, which you can do like so:
public class PlayerCharacter : Character { public float damageResistance = 0.3f; // Reduce damage taken by 30%. public override void TakeDamage(int amount) { int newAmount = amount * (1f - damageResistance); // Recalculate damage amount. base.TakeDamage(newAmount); } }
What is being done above is called overriding (hence the override
keyword in the method definition), which replaces the parent method with a newly-defined one. The new TakeDamage()
method reduces the damage that is dealt to PlayerCharacter
:
- The first line calculates the amount of damage dealt after applying
damageResistance
. It multiplies the original damage amount with the percentage of damage to be dealt (which is going to be less than 1) to reduce it. - The second line calls the parent
TakeDamage()
method defined inCharacter
to handle health reduction.
Note that you can only override methods that have been marked virtual
in the parent. More on that in the section immediately below.
In this way, the functionality of TakeDamage()
is extended in the PlayerCharacter
class — it adds a functionality on top of the generic TakeDamage()
that is unique to PlayerCharacter
. Note that it is not compulsory to invoke the base method when you are overriding. You can, for example, define a completely new TakeDamage()
method for PlayerCharacter
.
Abstract and virtual method modifiers
You can only override methods that have been marked either virtual
or abstract
in the parent. If you try to override the Start()
method defined in Character
(which has neither of the two modifiers), you will get an error when you try to compile your script (unless you add a virtual
modifier to the method).
The difference between a virtual and an abstract method is that abstract methods don’t have a function definition. Hence, they are only defined in child classes when they are overriden. If you look at the Attack()
method in Character
, you will see how abstract methods are declared.
Access modifiers
Note also that child classes are only able to access public
and protected
properties of their parents. They inherit private
properties, but you will not be able to access them from your script.
The Class Hierarchy
In large-scale game projects, scripts are often organised into a sprawling hierarchy, and almost all of the scripts that make the game work are related to one another by inheritance. In fact, if you take a look at the Unity Scripting Reference, you will find that many of Unity’s native components are related in this way too. Below are some examples:
BoxCollider
,SphereCollider
,CapsuleCollider
andMeshCollider
, the most basic colliders you can use in Unity, all inherit from aCollider
class, which defines common functionality that all colliders share.- The
SpriteRenderer
andMeshRenderer
components in Unity are responsible for most of the graphics in Unity 2D and Unity 3D games respectiely, and they both inherit from theRenderer
class, again outlining the common functionality they have (i.e. they need to have a functionality that draws them onto the screen).
Such an organisational structure gives the code consistency and makes it easier to manage and read.
If you are new to coding in such a manner, using another project as a reference is a great way to get an idea of how to put things all together. To this end, I’ve attached the Assets folder of a simple (but incomplete) 2D platformer that was built with hierarchy and inheritance in mind. To use it, create a new Unity project and replace its Assets folder with the provided one. A hierarchical map of how the classes of this project are organised can be found below, together with brief notes of what features are packed into each class and why.
Closing Notes
Inheritance is a programming concept that has been around for quite some time, and there is much, much more that can be done with it than what has been covered in this post. The core concepts to help you get started using it, however, have already been cramped into this post as simply and concisely as possible. Try and think about how you can put it to use to organise your code. Being a good programmer doesn’t mean you need to a lot about the topic — you simply need to have good organisation habits. Inheritance happens to be a simple and very effective way of helping you develop these habits.
The featured image for this post is drawn by Melcolm Lek. He is an excellent game artist with a decade of experience under his belt. You can see more of his works on DeviantArt.