Abstract Classes, And How They Speed Up Development
What is an abstract class? An abstract class is a base class that other classes can derive from, inheriting their properties and functions. It exists as an “Abstract” and cannot be placed in the scene hierarchy. In layman’s terms, it’s a script that serves as a template for other scripts where you can create all needed methods and variables, and any class that inherits this class automatically gets these variables and methods. Think of it like a cookie cutter. It creates the shapes for you, then you can decorate each out differently .Why is this helpful? Think about creating multiple types of enemies in a game. You know each enemy is going to have Health, Speed, XP... They Share all of the same stats but each one moves and attacks differently. So, you find yourself making multiple scripts and having to create the same variables and methods over and over. Utilizing abstract classes, you just need to create one Enemy class, and then, each enemy can inherit all of those variables and methods, and even make variations on them.
Lets start with some basic vocabulary so that you can follow along. We will use the below example to help define.
Inheritance: Receiving information in the form of methods and variables from another class. Inheritance works in a hierarchy, so if Enemy inherits from MonoBehaviour, and JumpingEnemy inherits from Enemy, JumpingEnemy will also inherit from Monobehaviour.
Base Class: The class being inherited from. By default, classes inherit from Monobehaviour, making MonoBehaviour a Base Class. Base Classes are also known as Parent Classes
Derived Class: The class doing the inheriting, Enemy is inheriting from MonoBehaviour, making Enemy the Derived Class. This dynamic changes depending on what class you are refrencing. If JumpingEnemy inherits from Enemy, then Enemy is JumpingEnemys Base Class and JumpingEnemy is the derived class. Derived Classes are also know as Child Classes
And for all you visual learners out there,
Lets take a look at how to set up and utilize abstract classes. Today, we will be looking at an abstract class I used for a switch. Basically, anything in my game that switches on and off, such as buttons, or light switches, utilizes this abstract class. I’m currently working on a VR space flight sim, which has dozens of different switches that will all be doing different things. So, the point of this class is to make different behaviors for the buttons very quickly.
Creating The Class
The first thing we do is create our abstract class. This can be any regular script, we simply type “abstract” before “class” and we are set. You can see below that this class inherits from MonoBehaviour. The class on the right of the colon is what your class is inheriting. The default for this is Monobehaviour, and it is important we keep it that way, so we can use functions like Start and Update.
Defining The Variables
In regular classes we have two basic options for our methods and variables, “Private “and “Public”. With an abstract class we will add another option called “Protected”. Lets break these all down before moving on.
Public: Can be seen and changed by any other class.
Private: Can be seen and changed only within the class it was created.
Protected: Can be seen and changed within the class it was created, and any derived classes.
Basically, anything we want private but still want access to in the derived classes we will now set to protected instead of private.
In this case, I know the Animator will not need to be manipulated outside of the base class, so I left it private. All other variables are protected, so that the derived classes can access them, but nothing else can. Serialized or Public field will show up in the derived scripts inspector automatically.
Creating The Methods
We also use a specific type of method in abstract classes different to what is used in a traditional class. These are called virtual methods. We use these rather than regular methods so that we can do something called overriding, which will be mentioned in a later section. We create virtual methods the same way we create any other method.
The distinction is the keyword “virtual”. It is important to note that virtual methods cannot be “Private”, and will throw you an error if you try to create them that way. They can however be “Protected”. All methods used in abstract classes need to be virtual in order to take effect. Any method can be virtual including MonoBehaviour methods, such as start and update which is a huge part of what makes these classes so powerful.
Inheriting an Abstract Class
So, we created an abstract class and some variables and methods to go in it. Now, how do we use it? The first thing you will notice is if you try to drop it on a game object you will get a pop up error.
This is because this script is abstract! It doesn't actually exist it is just a template for other scripts that can be inherited, we cant have any instances of it in the scene. To use this script we need to have another class inherit it. In this case I have a class called TestButton, that I want to derive from the Switches abstract class I created. We will simply change its base class from MonoBehaviour, to Switches.
We can see now, even without defining any variables, we have access to the variables from our base class!
We know that all virtual methods exist and are called on the derived class, but what if we want to change the functionality of what those methods do?
Saving the best for last, overriding a method is an incredibly useful tool that takes your abstract class from a time saving organizer to a dynamic, flexible, powerhouse of game development. This is the magic ingredient that ties everything together. Like the Virtual method we learned about, an Override method is a keyword that will utilize the virtual methods from our base class.
Protected overrides will show you a list of all your protected virtual voids in the tooltip, while public overrides will show all of your public virtual voids. Upon selecting a method it will autocomplete with base.methodName() in the body of the method, as shown below.
What you are doing here is modifying what the base class method does. In this case, the protected override will now perform all logic from the base class method before performing whatever logic you put after the base.OnLogic() completes. We can also delete base.Onlogic() so that it completely overrides the method and we don’t have any of the original functionality! For example, if we had our base class script as follows:
Then we can see how the below examples would react. (see the code snippets caption)
We are overriding the methods so that the same methods can do different things! In my Switches abstract classes case, I define all the logic for detecting input, playing sounds, and turning on/off in the abstract class. All I am doing in my individual derived switch and button classes is defining what happens when I turn them on and off.
Look how clean that code is! Believe it or not, this code is detecting when a VR controller is pressing the button, playing a noise, playing an animation, setting a delay on when the button can be pressed again, and flipping the button state to on. But, I defined all of that in the base class. Now, all I need to do is tell it what I want it to do when it gets turned on, and turns off — turn red or green. I now have a template that I can plug whatever logic I want done on top of the base logic and it just works! Want your button to rain confetti down from the ceiling and play music? 2 lines of code and your done. Want your button to start rotating your chair around in the spaceships cockpit?
Done. Its that easy. Once you get your abstract class set up, the rate in which you can implement variations of classes is exponential.
Abstract classes are an incredibly organized and efficient way of creating variations of scripts by defining a template that can be modified by the derived classes. I hope you learned something and can find a way to implement this in your games and programs.