Write better code with the Unity ScriptableObject

The power of the Unity ScriptableObjects cover

Hello my friend, so today you are going to learn about the most amazing, undiscovered, unused and underestimated feature of unity: The power of the Scritableobject.

You will discover that the scriptable objects are one of the most important features in Unity and you must use them as much as you can. As a matter of fact, we at Gladio Games use them in every project that we make.

But don’t worry, we are going to see what they are, why we use them and how to use them.

So, what is a Unity Scriptableobject? According to Unity, a ScriptableObject is a data container that you can use to save large amounts of data, independent of class instances. One of the main use cases for ScriptableObject is to reduce your Project’s memory usage by avoiding copies of values.

Well, as always the documentation is not that clear and for sure does not cover all the use cases that we have with the scriptable objects, so let’s go ahead and make the situation a bit clear.

Why use the Scriptableobject in Unity:

ScriptableObjects

  1. Build on top of the unity serialization system.
  2. The perfect tool for the game designer: In fact, game designers can use a Scriptableobject to make changes to the game without having to bother the poor programmer who is working on the game.
  3. Highly customizable.
  4. Reduce memory usage.
  5. Incredibly good for quick prototyping.
  6. Editor friendly.
  7. The Unity ScriptableObject can receive a callback from the unity engine (exactly like the MonoBehaviour).
  8. Modular: Systems do not directly depend on each other.
  9. When you make some changes to a scriptable object at run time, they are saved.

Yes, a Scriptableobject is like the holy grail of Unity, once you grasp the power of it, you can use them in basically every situation, but here are some examples of the use of a ScriptableObject within Unity:

  1. Organize your assets (audio, sprites, etc).
  2. As a custom event system.
  3. Custom and designer-friendly enum values.
  4. Easy to swap data without writing a single line of code.
  5. Customize player/NPC behaviour without touching the code.
  6. For saving and loading data.

Don’t worry, we will see some examples in this tutorial and I’ll assure you that when you will grasp the essence of the ScriptableObject you will be a much better and skilled game developer.

Getting Started with the Unity Scriptableobject:

To better understand a Scriptableobject, let’s start by making one.

To create a ScriptableObject, you simply need to make your class inherit from the Scriptableobject class.

Here is a very simple example:

public class ScriptableObjectExample : ScriptableObject
{

}

What are we doing here is very simple, we are telling Unity that we want to use the features of the ScriptableObject, as it would be with a MonoBehaviour.

But the difference is that you won’t use this script as a normal GameObject in the scene. Instead, the ScriptableObject is treated like any other asset that you create using the unity editor window.

And that is one of the biggest advantages of the scriptable objects, they are assets that you can create/change and hook into Gameobjects to make everything highly decoupled and customizable.

But don’t worry, you’ll see some clear examples later.

Now fill the script with some basic variables and to make the creation of the ScriptableObject even simpler, we are going to use the CreateAssetMenu attribute, so we can easily create a ScriptableObject in a few clicks from the Unity editor.

[CreateAssetMenu(fileName = "Data", menuName = "Example Scriptable Object", order = 51)]
public class ScriptableObjectExample : ScriptableObject
{
    public int exampleNumber;
    public Vector3[] examplespawnPoints;
}

Let’s start with the first line: Before you can create an implementation of the ScriptableObjectExample class, you can add the ScriptableObject to the Asset Menu, this will make the creation of the Scriptable Object much easier.

That is why I’ve used the CreateAssetMenu attribute with the following parameters:

  • fileName: The default name of the asset when is created.
  • menuName: The name of the asset as it appears in the Asset Menu.
  • order: The order of the asset within the Asset Menu. But keep in mind that unity separates assets into sub-groups by factors of 50. So by setting the order to 51 will put your new asset in the second grouping of the Asset Menu.

Now save the script and let’s test if it works.

Go to Assets –>Create and then you will see your new ScriptableObjectExample asset on the Asset Menu! And as expected, the asset is in the second group just underneath the Folder asset.

As you can see, setting the order to 51 results in our Asset just under the Folder asset

Click on the “Example Scriptable Object” and the Asset will be created:

The creation of a new Unity Example Scriptable Object Creation

As we have written on the CreateAssetMenu line, the name of the Asset is Data and if you inspect the asset, you can see that it contains the variables that we have inserted into the script.

And just to give you another tip, you can also create a new asset directly from the Project windows. Just right-click, Create –> Example Scriptable Object:

Create ScriptableObject from Windows folder

Quick copy assets:

A simple but useful tip: You can use the (Ctrl/Cmd + D)  to clone the assets:

That’s it! Now let’s go a bit deeper and see how we can use scriptable objects in practice!

Using Unity ScriptableObjects

Let’s see a simple example of how to use ScriptableObjects to quickly configure our game aesthetics.

The image above is from our last released game: Square Maze.

In Square Maze we had the following scenario: what if we want to change the colour of the maze, the background colour and the colour of all the elements making everything white in the maze?

Well, the quickest solution is to find all the prefabs and change the colour property of the sprite in each prefab present in the scene.

But what are the downsides of this solution?

  1. Starting with the obvious, you have to find all the prefabs in the scene and it’s really easy to miss a piece here and there, and even if you change the colour in each of the prefabs in your scene, the chance of missing a piece is really high.
  2. Even if you change all the prefabs, undoing the changes is a pain and it can easily lead to some broken features.
  3. And even if it works, what about loading a custom design while the game is running? We have to write everything in code and the chance of bugs is really high.

But don’t panic, this is where the ScriptableObject comes to help us.

With a simple Unity ScriptableObject we can easily define the layout of our game without touching a single prefab, plus we can load custom layout at run time, and our designer can try a new layout by simply creating a new layout and attaching it into our scene.

Starting with an example, let’s create a simple ScritableObject to define the layout of some objects into our scene:

using UnityEngine;
using System.Collections.Generic;
[CreateAssetMenu(fileName = "Maze Layout", menuName = "Level Design/Graphics/Maze Configuration")]
public class MazeConfig : ScriptableObject
{
    [SerializeField] private Color _mazeColor = Color.black; // 1
    [SerializeField] private Color _backroundColor = Color.black; // 2

    #region Color the maze
    public void ConfigMaze() // 3
    {
        SetMazeColor();
        SetBackgroundColor();
    }

     private void SetMazeColor() //4
    {
        List walls = new List(GameObject.FindGameObjectsWithTag(ProjectIds.TagIDs.Wall));
        walls.RemoveAll((GameObject a) => a.gameObject.GetComponent() != null);
        foreach (GameObject gameObject in walls)
        {
            if (gameObject.GetComponent() != null)
            {
                gameObject.GetComponent().color = _mazeColor;
            }
        }
    }

    private void SetBackgroundColor() // 5
    {
        Camera.main.backgroundColor = _backroundColor;
    }
    #endregion
}

As you can see the class is very simple:

  1. A property to identify the colour of the Maze
  2. A property to identify the colour of the Background

Both of these properties have the [SerializeField] attribute.

The [SerializeField] attributes means that you have private script variables that are exposed in the Inspector. This will let you set the values in the editor without giving access to the variables from other scripts.

3. ConfigMaze() is the method that is called to set the colour of the maze.

4. SetMazeColor() Is where we find all the walls that compose the maze and change the colour of each wall to the colour selected

5. SetBackgroundColor() Same thing for the background which is set in the main camera.

One very interesting thing about this ScriptableObject is that the logic to set the colour of the GameObjects is inside the ScriptableObject.

This is very good because you can move some logic from your game managers directly into the ScriptableObject, with the advance to have:

  1. Less coupled classes/code.
  2. Better use of the single responsibility pattern.
  3. And as we will see in a bit, by moving the logic inside the ScriptableObject we can run the just written code directly in the Unity editor without having to add the ScriptableObject into the scene!

But moving on with our examples, let’s create the asset and see it in action.

Once the asset is created, in the inspector you’ll have something like this:

Inspectable scriptableobject

In this case, I’ve set the background colour to red. Now I just need a script that takes the ScriptableObject and used it inside our game.

Let’s create a new very simple class to use our ScriptableObject:

public class GameLayoutManager : MonoBehaviour
{
    #region Fields
    [SerializeField] private MazeConfig _levelLayout;

    #endregion

    #region Monobheavior
    void Start()
    {
        _levelLayout.ConfigMaze();
    }
    #endregion
}

There is not much to say here, we just import our MazeConfig ScriptableObject and on the Start we call the ConfigMaze() to set the background colour and the colour of the maze.
Let’s test it:

And if we hit the play button:
ScriptableObj in Unity Test
It just worked!
Now if  you want to change the layout without touching the one in use, you can simply create/clone the current layout, make the changes that you want and drag the new layout into the scene.
But what are the advantages of this approach?

  1. If you want to try new layouts, you don’t need to touch the current one and this is very good in case you have a very complex layout in place and you don’t’ want to touch it.
  2. No code is needed and this is very good because the designers are independent of the developers.
  3. You can easily load new layouts at run time! Imagine you need a store where the user can buy new layouts of the game, well using a ScriptableObject is the best solution because with the minimum effort you can have a simple loading system to load the requested Assets into the game.
    1. Plus this solution is highly expandable, in fact, if you want to add new layouts, you can simply make a new one within a few clicks without having to touch a line of code.

Also, by using the ScriptableObject like in this case, you can prototype without having to touch the code and this leads to a very robust system and which is less prone to errors.

Extending ScriptableObject with Unity editor functionality

But wait a moment, what if I want to change the layout of my game directly from the Window panel without having to drag my ScriptableObject into the scene. Can I do that?

Well of course, we need to leverage the power of the Unity editor and trust me this is really simple.

let’s see this magic script:

[CustomEditor(typeof(MazeConfig),true)] // 1
public class ConfigMazeEditor : Editor // 2
{
    #region Draw Inspector
    public override void OnInspectorGUI() 
    {
        base.OnInspectorGUI();
        EditorGUI.BeginDisabledGroup(serializedObject.isEditingMultipleObjects);
        if (GUILayout.Button("Config Maze")) // 5
        {
            ((MazeConfig)target).ConfigMaze(); // 6
        }
        EditorGUI.EndDisabledGroup();
    }
    #endregion
	
}

We won’t go into the details on how to customize the editor windows because it is beyond the scope of this tutorial, but with this little script we can create a magic button that can run our code directly from the Unity windows:

But what the script does is something really simple, It’s “attach” a button to our ScriptableObject so we can run our code to configure the layout just by clicking the button.

Do you see that little “Config Maze” button? With that button we can now configure our layout directly into the Windows without the need to create any class to load our ScriptableObject and we are totally independent from the scene view!

And this was a little example to show you the power and versatility of the scriptable objects.

The ScriptableObject Cheatsheet

Get now the exclusive ScriptableObject Cheat Sheet for free. I’ve created an exclusive cheat sheet with all you need to know about the ScriptableObjects! Join our newsletter and download the ScriptableObject CheatSheet

The Unity ScriptableObject callbacks

Another important aspect of the ScriptableObject is that they can receive callbacks exactly like a MonoBehaviour.

These are the callbacks supported by the MonoBehaviour:

  • Awake
  • OnDestroy
  • OnDisable
  • OnEnable

Those callbacks gave just more power and versatility to the usage that we can do with the ScriptableObjects.

Inventory example

This is another example directly from Unity. What you see above is a very good way to manage an inventory system.

This example shows us a very good and clean architecture in which the NPC, Player and the Equip Screen know nothing about each other, they are completely independent and decoupled. Plus the Save System is completely unaware of the Player and the other systems, it just uses the Inventory to save all the data that it needs.

Conclusion

I hope I was able to give you a taste of the incredible power of the ScriptableObject, you can use them for basically everything and are the perfect tool to have a better structure and decoupled code and in general to give a better structure to your project.

Well, if you’re starting to like the power of the Scriptableobject, then let me suggest three Unity talks that you really must see:

  1. Overthrowing the MonoBehaviour Tyranny in a Glorious Scriptable Object Revolution
  2. Game Architecture with Scriptable Objects

  3. Making Cool Stuff With ScriptableObjects

See you on the next tutorial!

A big hug

Mark

Written By
More from Marco

Flow channel in games: Make the player Feeling awesome

The Flow channel in games: Most of the work of a game...
Read More

Leave a Reply

Your email address will not be published. Required fields are marked *