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.
using UnityEngine;
// creating asset menu shows scriptable object in editor menu. We will be able to create this object from there
[CreateAssetMenu(fileName = "CharacterStats", menuName = "CustomScriptableObject/CharacterStats")]
public class CharacterStats: ScriptableObject {
// properties of character
[SerializeField] private float health;
[SerializeField] private float armor;
[SerializeField] private string name;
// Deal damage, damage will be reduced by armor
public void Damage(float damage) {
damage = damage - armor;
if (damage < 0) return;
health -= damage;
}
// get current health
public float GetHealth() {
return health;
}
// get player's name
public string GetName() {
return name;
}
}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.
public class CharacterDamageDealer: MonoBehaviour {
public CharacterStats characterStats; // reference to scriptable object asset asset
// deal damage to player
public void CharacterHit() {
characterStats.Damage(10);
}
}
// UI system to show stats
public class CharacterUI: MonoBehaviour {
public CharacterStats characterStats; // reference to scriptable object asset asset
public TextMeshProUGUI health;// text to display health
public TextMeshProUGUI characterName;// text to display name
private void Update() {
health.text = characterStats.GetHealth().ToString();
characterName.text = characterStats.name;
}
}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.
public event Action < float > onPlayerDamaged; // event to notify damage
// Deal damage, damage will be reduced by armour
public void Damage(float damage) {
damage = damage - armour;
if (damage < 0) return;
health -= damage;
onPlayerDamaged?.Invoke(health);
}// subscribe to damage event of player at start
private void Start() {
characterStats.onPlayerDamaged += OnHealthChanged;
}
//change health when damage is done
private void OnHealthChanged(float currentHealth) {
health.text = currentHealth.ToString();
}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.
// abstract class to implement buff
public abstract class PlayerBuff: ScriptableObject {
// modifies damage dealt
public abstract float ModifyDamage(float 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
// this buff heals amount equal to damage
[CreateAssetMenu(fileName = "Healing", menuName = "CustomScriptableObject/Buff/Healing")]
public class Healing: PlayerBuff {
public override float ModifyDamage(float damage) {
if (damage > 0) damage = -damage; // make damage negative so that it will heal
return -damage;
}
}The second buff we will create is Weakness . This negative buff will increase damage taken proportional to weaknessAmount .
// it increases damage by
[CreateAssetMenu(fileName = "Weakness", menuName = "CustomScriptableObject/Buff/Weakness")]
public class Weakness: PlayerBuff {
public float weaknessAmount;
public override float ModifyDamage(float damage) {
damage *= weaknessAmount; // increasing damage
return damage;
}
}The third buff is Armor. This buff decreases incoming damage by armor . Also, it removes armor every time damage is taken.
// decreases damage
[CreateAssetMenu(fileName = "Armor", menuName = "CustomScriptableObject/Buff/Armor")]
public class Armor: PlayerBuff {
public float armor;
public override float ModifyDamage(float damage) {
damage -= armor; //decreases damage by armor
return damage > 0 ? damage : 0; //make sure damage is always positive
}
}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.
public List < PlayerBuff > playerBuffs;
// Deal damage, damage will be reduced by armour
public void Damage(float damage) {
// modify damage according to each buff
foreach(PlayerBuff buff in playerBuffs) {
damage = buff.ModifyDamage(damage);
}
// damage health
health -= damage;
onPlayerDamaged?.Invoke(health);
}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!