What are Scriptable Objects?
In Unity, while
MonoBehaviour objects are stored in memory and exist only while playing the game; Scriptable Objects exist in the project itself and are stored in serialised form. They are used as a container to store values.
A scriptable object exists even when the game is stopped and persists between play modes. Values are retained between edit and play mode. Scriptable object assets can be created and stored like other assets such as images, materials, etc.
Creating Scriptable Objects
To make a scriptable object, we must create a class that inherits from the
ScriptableObject class. It is also helpful to create a
CreateAssetMenu header from the editor menu.
In this example, we will create a
CharacterStats class to demonstrate the creation and use of a scriptable object. It will be used to store the status of a character. It consists of the
Let's create getters for those. Also, we will be adding a
Damage method so that health can be decreased. Incoming damage will be decreased by
armor amount before decreasing
Now, we can create the asset from
Uses of Scriptable Objects
Since scriptable object assets exist in the project, they can have use cases that are not possible with normal
MonoBehaviour objects. Let's discuss some of these use cases.
Scriptable objects assets can be used to make global objects that can be accessed by multiple objects.
For example, let's create a
CharacterUI class that will reference the scriptable asset we created earlier. Since they use the same asset, values will be the same for both. This can also be done from a
static class, but static classes can have only one instance, and their value can't be viewed or edited from the editor menu.
Events can be invoked from scriptable assets and listened to by other classes that reference them. These events can be used to detect when the state of the game is changed.
Let's add an
onplayerDamaged event in
CharacterStats so that the player can get notified when their character takes damage.
CharacterUI will listen to this event so that it can change the UI when the player takes damage. Doing this will eliminate the need to continuously update text in
Abstract Scriptable Objects
Making an abstract scriptable object class will allow us to create multiple variations of a single class. These objects can be plugged in from the editor, allowing us to change their behaviour from the editor itself.
For demonstration, let's create a buff system that will modify the damage taken by the player. First, create an
PlayerBuff class that contains a method to modify damage.
Now create various buffs by extending from the abstract class. The first type of buff is healing. It just converts damage into healing. Let's call it
The second buff we will create is
Weakness . This negative buff will increase damage taken proportional to
The third buff is
Armor. This buff decreases incoming damage by
armor . Also, it removes
armor every time damage is taken.
Now, let's modify the
Damage method of the
CharacterStats class so that all buffs can be applied. We will store all buffs of the player in the
playerBuffs list of the type
Notice that we are creating a list of abstract classes so that all types of buffs can be stored.
After that, all we need to do is create assets from it and place them in the character's stats list. I have created two
Poisoned , two
LowArmor and a
Healing type. These buffs can be just dragged and dropped in the player's status to apply them. Whenever a player takes damage, it will be modified before decreasing the player's health.
Preserving Values Across Play and Edit Modes
Values changed in the in-game mode are retained while exiting play mode. This can be useful when we want to try out different values while the game is running. This is not possible using
GameObjects as their values get reverted when the game is stopped.
Reducing Memory Usage
Every time a prefab is created in a scene, it creates a clone in memory. This may cause a waste of memory when the same prefab is instantiated multiple times. Suppose you have a prefab of an enemy that has the stats
damage . Let's say 20 of them exist in a scene. Those 20 enemies have 20 instances of redundant
damage in memory.
This problem can be solved using scriptable objects. You can create a scriptable object with
damage property. All instantiated enemies can now refer to the same scriptable object. This creates only one copy of the value in memory, reducing usage. If you want another enemy type with a different value, you can just create another scriptable asset and use it.
Today we discussed the creation and use of scriptable objects. Building a game on the scriptable object architecture will optimise it and make it flexible to change. Besides the above uses, it has many other applications such as pluggable AI, state management, etc.
That's all for today, thank you for reading!