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 Player Character and Enemy Character 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 : MonoBehaviour Character
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 can do.
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.
Article continues after the advertisement:
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:
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 in Character to handle health reduction.
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
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.
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. 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.
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.
Article continues after the advertisement: