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 health
, name
and armor
attributes.
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 health
.
Now, we can create the asset from Asset
> Create
> CustomScriptableObject
.
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.
Global Variables
Scriptable objects assets can be used to make global objects that can be accessed by multiple objects.
For example, let's create a DamageDealer
and 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.
Game Events
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 Update
call.
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 abstract
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 Healing
The second buff we will create is Weakness
. This negative buff will increase damage taken proportional to weaknessAmount
.
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 PlayerBuff
.
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 Weakness
type: Curse
and Poisoned
, two Armor
types: HighArmor
and 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 maxHealth
and damage
. Let's say 20 of them exist in a scene. Those 20 enemies have 20 instances of redundant maxHealth
and damage
in memory.
This problem can be solved using scriptable objects. You can create a scriptable object with maxHealth
and 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.
Conclusion
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!