Visualisation of inheritance in Unity

Inheritance in Unity

In Advanced Scripting by John FrenchUpdated 2 Comments

This article is part of a series on Design Patterns in Unity.

One of the most useful aspects of object-oriented programming, which Unity uses, is Inheritance, which allows one class to use, or inherit, the functionality of another.

Typically, you’d use inheritance when you want to create different classes that are a type of something else.

A Player, an Enemy, and an NPC, for example, would all behave in different ways, but they might still need to share a lot of the same functions, such as the ability to talk or walk around.

In which case, making them all a type of Human, by using inheritance, can be extremely useful, as it allows you to make each class work in a different way, while still sharing the functions that are common to all of them at a base level.

However, if you’re new to Unity, understanding how to manage the relationship between classes that inherit from each other can be a little tricky.

So how does inheritance in Unity work, what is it for, and should you use it in your project?

How does Inheritance in Unity work?

A derived class, which is a class that inherits from a different class, is able to use all of its parent’s public variables and functions as if they were its own.

This can be useful for sharing functionality between different versions of the same type of thing.

For example, a Player script that inherits from a Human base class can access a public health variable from its parent as if it exists in its own class.

Like this:

public class Player : Human
{
    private void Start()
    {
        Debug.Log("I have " + hp + " health!");
    }
}

public class Human : MonoBehaviour
{
    public float hp;
}

The variable will even appear in the Inspector as if it exists in the player class.

Screenshot of an inherited variable appearing in the Inspector

Inherited variables will appear in the Inspector before local variables.

To make a class inherit from another class, simply type out the name of the class you want it to inherit from after the definition, and separated by a colon.

Like this:

public class DerivedClass : BaseClass
{
    ...
}

Typically, to do this, you’ll be replacing the default base class which, for scripts that are attached to objects as components, is usually going to be Monobehaviour.

Monobehaviour is what allows a script to be attached to an object as a component and it means that event messages, such as Start, Awake, and Update will be called on it.

However, when you use inheritance, that functionality isn’t lost.

In this example, the Human class still inherits from Monobehaviour, meaning that, technically, the Player class does too.

This is because, while a class can only inherit from one other class at a time, it can inherit from a hierarchy of many classes spanning multiple layers, similar to generations in a family tree.

Which can be useful, as it allows you to further separate class hierarchies by the properties and functions that make each one unique.

For example, if you wanted to, you could create another level above Human, such as an Animal Class, which you could use to create additional branches.

Like this:

public class Player : Human
{
    private void Start()
    {
        Debug.Log("I have " + hp + " health!");
    }
}

public class Cat : Animal
{
    void Pounce()
    {
        // Do cat things
    }
}

public class Human : Animal
{
    void DoTaxes()
    {
        // Do human things
    }
}

public class Animal : MonoBehaviour
{
    public float hp;
}

Why is that useful?

All animals have health, but humans and cats spend their days doing very different things.

Meaning that, while the functionality they share is accessible to both classes, the behaviour that makes them unique only exists in theirs.

This allows you to identify, and use, a specific entity, such as a cat, but with a general type, such as an animal.

So when would you realistically use this?

When should you use Inheritance in Unity?

The idea behind inheritance is to give the same functions and properties to objects that are different but that are also the same type of thing, for two main reasons.

Firstly, it helps to avoid writing the same code twice, since objects that do the same thing can inherit their shared behaviour from a base class instead of keeping it in their own.

An example of this would be writing code to make an object jump in both a Player and Enemy script.

If you wanted to change how jumping worked, you’d have to edit both scripts to make one change.

Instead, it makes more sense to have each script inherit from a base class, such as a Human class, and put the jumping mechanism in there.

Visualisation of inheritance in Unity

Inheritance allows you to create different specific classes that share the same functionality because of their type.

Then, if you need to change how jumping works in general, you only need to do it in one place.

The other reason for using inheritance is to allow a specific class to be treated in a general way.

For example, it’s possible to create a function that accepts a Human class as an argument and then pass an Enemy or a Player into that.

This works because the enemy and player classes derive from the human type, meaning that they will either have the same access to the properties and functions that are already in the human class, or they will implement their own versions of them.

The benefit of this is that it’s possible to pass an enemy or a player into a function that’s expecting a human.

This is known as Subtyping, which is a form of Polymorphism, which is one of the fundamental features of object-oriented programming.

What is Polymorphism in Unity?

Polymorphism is the use of a single interface to represent many possible different types.

In Unity, a common use of polymorphism is Subtyping which, generally speaking, involves allowing a derived class to be used where a base class is expected.

You’ll find examples of subtyping in many of Unity’s own functions and features.

For example, there are many different types of Collider, such as box colliders and sphere colliders) that each work in a slightly different way.

But, fundamentally, they all inherit from the Collider base class, meaning that physics functions can use and return any type of collider that they interact with, so long as it inherits from the same base class.

This can be extremely useful when you want to use a type of something, but you don’t necessarily know what it will be.

So what does that look like in a project?

Let’s say that you have a Shape base class that holds a function that counts how many sides that type of shape has. 

Like this:

public class Shape : MonoBehaviour
{
    public void CountSides()
    {
        Debug.Log("I don't know how many sides I have!");
    }
}

You could use the Shape base class to create other types of shape, such as a Circle, a Square or a Triangle.

Like this:

public class Triangle : Shape
{
    public void CountSides()
    {
        Debug.Log("I have 3 sides!");
    }
}

public class Square : Shape
{
    public void CountSides()
    {
        Debug.Log("I have 4 sides!");
    }
}

public class Circle : Shape
{

}

Some of these have the same Count Sides function and some of them don’t, but what’s important is that they all inherit from the same Shape base class.

This means that you can create a parameter or a variable that is a type of Shape, and pass any of these scripts into it, and it will work.

Like this:

public class SubtypeExample : MonoBehaviour
{
    public Shape shape;

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            shape.CountSides();
        }
    }
}

It doesn’t matter if you drag a circle, a square, or a triangle to the shape field.

Because they are all shape types, the Count Sides function exists in all of them via its Shape base class, even if the specific type of shape doesn’t implement its own version of the method.

And, if it does contain a new, local version of the count sides function, it can use that instead of the base version.

But, not by default…

As it is right now, if you call the Count Sides function on a shape type, it will only call the base version of that function from the shape class, even if the script you pass in has a specific, local version of the same method.

This happens because the reference is still, fundamentally, a shape, and nothing more. There’s no connection between the function that exists in the base class and the classes that inherit from it.

Example of polymorphism being used to use a derived class in Unity

As it is right now, if you call count sides on a Square script that’s passed into a Shape field, it’ll display the “I don’t know how many sides I have” message, even though the square class has its own version of the function.

Meaning that, in this case, you’ll only be able to call the specific square or triangle versions of the count sides function if you define a type of square or triangle specifically, not a shape in general.

So how can you call a generic function on a base class and get the specific version back?

That’s where Overriding comes in.

Overriding in Unity

Overriding in Unity allows you to replace the original implementation of a function with a version of the same type that exists in a derived class.

This can be extremely useful, as it allows you to call a function on a base class without knowing exactly what type of derived class is going to use it.

The benefit of this is that different scripts can implement the same function in different ways, meaning that you can change a result, without changing the trigger.

However, this doesn’t happen automatically.

There are two steps to overriding a function in Unity, Virtual and Override.

Virtual and Override in Unity

To override a function from a base class, the function that’s going to be overridden must be Virtual.

This is what allows it to be replaced by a function in another class.

Like this:

public class Shape : MonoBehaviour
{
    public virtual void CountSides()
    {
        Debug.Log("I don't know how many sides I have!");
    }
}

It also needs to be either Public or Protected, so that deriving classes can access it.

Next, the derived function that’s going to replace the original must use the Override keyword.

Like this:

public class Square : Shape
{
    public override void CountSides()
    {
        Debug.Log("I have 4 sides!");
    }
}

If it doesn’t, then it won’t be used in place of the virtual method and the base version of the function will be called instead.

Then, even if you only have a reference to a type of shape, the specific type’s override function will be called in place of the original.

Example of polymorphism being used to use a override a function in Unity.

Because the Square class uses an overriding version of the Shape’s virtual function, that’s the version of the method that’s called.

It’s important to remember that overriding doesn’t extend the base function, it completely replaces it, meaning that the original will not be called.

Which can sometimes be useful.

But, if you do still want to call the base version of the function, along with the override replacement, then you can.

You just have to do it manually.

How to call a base function manually when overriding in Unity

When using override functions, the original virtual method is not called if an overriding method exists.

However, there may be times when you want to extend a function, not replace it completely.

This can be done by manually calling the original virtual method inside the override version by using the base keyword.

Like this:

public override void Walk()
{
    base.Walk();
}

The point of overriding a function is so that a specific class can use a unique version of a method without changing what the trigger is.

For example, you could create an animal base class that handles walking, and then have different types of animals inherit from it.

Then, even though cats and humans, for instance, walk in different ways, you’d be able to trigger either of them using a single input, the Walk Function, without needing to know which it was.

Like this:

public class Cat : Animal
{
    public override void Walk()
    {
        Debug.Log("Walk like a cat");
    }
}

public class Human : Animal
{
    public override void Walk()
    {
        Debug.Log("Walk like a human");
    }
}

public class Animal : MonoBehaviour
{
    public virtual void Walk()
    {
        Debug.Log("Walk like an animal");
    }
}

This method of attaching variable responses to a universal trigger is similar to Interfaces, which create a functional requirement for a script to include certain methods, meaning that other scripts can use them based on what they can do, not what they are.

Examples include interactable objects, collectable objects and other types of class that may need to be treated in the same way, even if they do different things as a result.

However, while they work in similar ways, there are differences between interfaces and inheritance, and what you should use them for.

An Interface, for example, forces an object to behave in a certain way so that other scripts can interact with it without knowing exactly what it is.

Meaning that interfaces define what a script is able to do.

Inheritance, on the other hand, is hierarchical, it’s based on what a class is, not what it does.

Because of this, the relationship between a class and the functions it inherits is optional.

Meaning that inheriting classes don’t have to implement the virtual methods in the classes that they inherit from.

That is, unless you force them to.

Abstract functions in Unity

An abstract function, or class, in Unity is a type that must be implemented by an inheriting type.

For example, an abstract class can’t be used as an instance, you’ll need to create a script that inherits from that abstract type and then use that instead.

Like this:

public class AbstractExample : BaseClass
{
    private void Start()
    {
        BaseFunction();
    }
}

public abstract class BaseClass : MonoBehaviour
{
    public float number;

    public void BaseFunction()
    {

    }
}

This can be useful when you want to create multiple different types of a thing, but without using the base type directly.

Abstract Functions work in a similar way to abstract classes.

They force an inheriting class to implement a version of the method they’re overriding that has the same name and argument pattern as the original.

Like this:

public abstract class Shape : MonoBehaviour
{
    public abstract void CountSides();
}

Because the original function is abstract, and can’t be called directly, it doesn’t need to have its own body.

Likewise, because it contains an abstract function, the class itself must also be abstract, meaning that you must create a derived class in order to use it.

The benefit of this, however, is that you can define any type of object, such as a Shape, without knowing what the shape is going to be, and pass any class that inherits from it as an argument in a function or as a reference in a variable, and it will just work.

Now it’s your turn

Now I want to hear from you.

How are you using inheritance in your game?

Are you overriding functions, creating abstract classes or are you avoiding inheritance entirely?

And what have you learned about using inheritance in Unity that you know someone else would find useful?

Whatever it is, let me know by leaving a comment.

John French profile picture

by John French

Game audio professional and a keen amateur developer.

Get Game Development Tips, Straight to Your inbox

Get helpful tips & tricks and master game development basics the easy way, with deep-dive tutorials and guides.

How this content was created

This article was written using first-hand research and experience, without the use of AI. For more information on how Game Dev Beginner articles are written, see my writing policy.

Comments

  1. Your articles are just priceless wonder John!
    This is a miracle how simply and accurately you explain not such an easy things.
    Thanks a lot! And please keep them coming.

Leave a Comment