Scriptable Objects

This article is about the basic understanding on scriptable objects in Unity, including why, when and how to use scriptable objects.

What is scriptable objects

Scriptables objects are amazing data containers in Unity. Here is the official introduction to it.

ScriptableObject is a class that allows you to store large quantities of shared data independent from script instances.

Why we use scriptable objects

As the data containers, scriptable objects are stored in assets instead of attached to a GameObject in a scene. That’s also the biggest benefit we are going to use scriptable objects. Scriptable object has only one instance, which means all references to it from different objects and scenes are referenced to the same instance. That’s why scriptable objects are meant to save data, and the data here can be used by different objects.

Before we know about scriptable objects, what we usually do is to create a prefab and set the value in the inspector. Although it exactly works and can be used as the data containers, there are several points that scriptable objects work better.

  1. Scriptable objects are asset files, and can be managed like other assets. For example, the value changed in playing mode will be saved.
  2. By using scriptable objects, all references are to the same instance, which satisfies a more general idea of shared data. Also, this can also save a considerable amount of memory. For example, supposed we make a prefab with a component that contains an array of a million integers. The array occupies 4MB of memory and is owned by the prefab. If you instantiate 10 instances of the prefab, which means a comsuption of 40MB memory for the arrays. Instead the array is considered as shared data and stored as a scriptable object, the momery consumed will be always 4MB.
  3. In some of the case, it’s unreasonable to save some data as a prefab. In other words, it doesn’t fit the general idea. For example, level setting.

More offen, scriptable objects are only meant to save data, but can also be used to help serialize objects and can be instantiated in our scenes.

How to use scriptable objects

Here we use scriptable objects to realize a simple ability system.

Abstract Ability Class (Scriptable)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public abstract class Ability : ScriptableObject {

public string aName = "new Ability";
public Sprite aSprite;
public float aCoolDown = 1f;
public KeyCode aKey;

public abstract void Initialize(GameObject obj);
public abstract void TriggerAbility ();
}

Shoot Bullet Ability (Derived from Ability)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(menuName = "Abilities/ShootNormalBullet")]
public class ShootNormalBullet : Ability {

public GameObject bullet;
public float bulletSize;
public Sprite bulletSprite;
public float bulletSpeed;

protected Transform player;
protected GameObject newBullet;

public override void Initialize (GameObject obj){
player = obj.GetComponent<Transform> ();
}

public override void TriggerAbility (){
newBullet = Instantiate (bullet, player.position, Quaternion.identity);

newBullet.transform.localScale *= bulletSize;
newBullet.GetComponent<SpriteRenderer> ().sprite = bulletSprite;
newBullet.GetComponent<Rigidbody2D> ().velocity = (Camera.main.ScreenToWorldPoint(Input.mousePosition) - player.position).normalized * bulletSpeed;
}
}

CreateAssetMenu allows us to directly create the corresponding asset file in editor. After we create our ShootbulletAbility asset file, we can easily modify the setting in the inspector. And this asset can be easily referenced in different scene. (You can drag it to a public Ability field of a component, or use Resurce.FindObjectsOfTypeAll() to dynamically reference to it).

When to use scriptable objects

Data Objects

As the data containers, scriptable objects are usually used to save game’s data. For example, level setting, ability data, character data and so on. Here we give an example of inventory item list.

InventoryItem

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using UnityEngine;
using System.Collections;

[System.Serializable] // Our Representation of an InventoryItem
public class InventoryItem
{
public string itemName = "New Item"; // What the item will be called in the inventory
public Texture2D itemIcon = null; // What the item will look like in the inventory
public Rigidbody itemObject = null; // Optional slot for a PreFab to instantiate when discarding
public bool isUnique = false; // Optional checkbox to indicate that there should only be one of these items per game
public bool isIndestructible = false; // Optional checkbox to prevent an item from being destroyed by the player (unimplemented)
public bool isQuestItem = false; // Examples of additional information that could be held in InventoryItem
public bool isStackable = false; // Examples of additional information that could be held in InventoryItem
public bool destroyOnUse = false; // Examples of additional information that could be held in InventoryItem
public float encumbranceValue = 0; // Examples of additional information that could be held in InventoryItem !!!
}

InventoryItemList

1
2
3
4
5
6
7
8
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class InventoryItemList : ScriptableObject
{
public List<InventoryItem> itemList;
}

In my view, we can use prefab to realize a general character behavior and plugs different scriptable objects in. By this we can obtain different kinds of character with a low comsumption of memory.

Delegate Object

In scriptable objects, you can also define some methods, and pass the attached monobehavior (or GameObject) to it. It’s like a slot design model. Monobehavior provides slot and scriptable objects plugs in. This is quite common in AI, buff/debuff or a charge process.

The ability system above is such a case.

Pay Attention

Because all references for scriptable objects share the same instance. You should be care about what kinds of fields to be defined in the class. The values here should always remain the same. For example, maxHP, maxMP, monsterName can be set in scriptable objects. On the contrary, the currentHP should be better defined in the attached monobehavior.

Reference

Ability System with Scriptable Objects
Character Select with Scriptable Objects
【Unity】ScriptableObject的介绍
Offcial Documentation