ScriptableObject 基礎

ScriptableObject 是一個資料容器(Data container),它透過共享通用資料的方式來減少對重複資料的記憶體佔用,進而降低你APP的記憶體使用量。

如果你的預製物件(Prefab)中儲存了一些不會改變的資料,那麼很適合把它們改為使用 ScriptableObject ,因為每當你實體化(instantiate)一個預製物件(Prefab)時,這個實體會保存自己的一份資料,當實體化很多個實體時,會有很多重複的資料佔用很多記憶體,因此你可以使用 ScriptableObject 可以把資料儲存起來,然後讓所有的預製物件(Prefab)存取這個 ScriptableObject

ScriptableObjectMonoBehaviour 一樣,繼承了 UnityEngine.Object ,但是與 MonoBehaviour 不同在於你不能把 ScriptableObject 附加(attach)到一個 GameObject 上,也不能進行 GatComponent 等對 GameObject 的操作,相反你需要把它儲存為資源(Asset)。

在使用Unity編輯器時,你可以把資料放在 ScriptableObject ,在執行時也可以調整其中的資料,但是你不可以把它當作遊戲存檔的工具。

使用 ScriptableObject 主要情況是在編輯時期修改 ScriptableObject 的資料,在執行時這些資料會被當作資源(Asset)使用。

使用方式

建立一個類並繼承 ScriptableObject,你可以使用 CreateAssetMenu 屬性(attribute),讓你方便在編輯器中使用,以下是一個範例:

1
2
3
4
5
6
7
8
[CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/SpawnManagerScriptableObject", order = 1)]
public class SpawnManagerScriptableObject : ScriptableObject
{
public string prefabName;

public int numberOfPrefabsToCreate;
public Vector3[] spawnPoints;
}

之後,你可以在 Assets -> Create -> ScriptableObjects -> SpawnManagerScriptableObject 中建立新的 ScriptableObject 實體。

你可以更改該實體的名稱與它的屬性。

接下來你可以在 MonoBehaviour 中使用這個 ScriptableObject 實體,如下

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
28
29
30
31
32
33
34
35
using UnityEngine;

public class Spawner : MonoBehaviour
{
// 要實體化 Prefab 的參考
public GameObject entityToSpawn;

// 一個 ScriptableObject 的參考
public SpawnManagerScriptableObject spawnManagerValues;

// 記錄了實體化了多少個實體
int instanceNumber = 1;

void Start()
{
SpawnEntities();
}

void SpawnEntities()
{
int currentSpawnPointIndex = 0;

for (int i = 0; i < spawnManagerValues.numberOfPrefabsToCreate; i++)
{
// 使用 ScriptableObject 中的資料
GameObject currentEntity = Instantiate(entityToSpawn, spawnManagerValues.spawnPoints[currentSpawnPointIndex], Quaternion.identity);

currentEntity.name = spawnManagerValues.prefabName + instanceNumber;

currentSpawnPointIndex = (currentSpawnPointIndex + 1) % spawnManagerValues.spawnPoints.Length;

instanceNumber++;
}
}
}

在Unity編輯器中,使用拖拉的方式將剛剛建立好的 ScriptableObject 實體拖拉到對應的位置上,另外 Box Prefab 是一個簡單的Square

最後在執行Unity,就可以看到Unity使用剛剛建立出來 ScriptableObject 資料去產生 Box 物件。

此外除了在編輯器中建立 實體,在執行期間也可以透過 CreateInstance 來建立。

1
ScriptableObject.CreateInstance<MyScriptableObjectClass>();

MonoBehaviourScriptableObject 的比較

MonoBehaviour ScriptableObject
MonoBehaviour 接收所有來自 Unity 的 callback,像是 StartAwakeUpdateOnEnable ,OnDisablOnCollisonEnter ScriptableObject 只接收一些: AwakeOnEnableOnDestroyOnDisable 。 在 Editor 中的話還有 OnValidateReset
MonoBehaviour 必須要 附加(attach)到 GameObject ScriptableObject 不能附加到 GameObject 上,需要在專案(Project Level)中將它們儲存為 asset 檔案,在其他腳本中參考這些 ScriptableObject asset
當儲存 MonoBehaviour 時,會將它們的資料儲存到 Scenes 與 Prefabs 中 每個 ScriptableObject 實體都會被保存為專案層級(Project level)的獨立檔案中
一般在 Play Mode 中修改 MonoBehaviour 中的值之後離開 Play Mode 的話,這些值會被重新設定(Reset) 當離開 Play Mode 後, ScriptableObject 則不會重新設定。此外當編譯發布之後,執行期間 ScriptableObject 修改的值不會被保存,而是為發布時的值

ScriptableObject 中只有以下的 callback 會被呼叫

  • Awake : 與 MonoBehaviour 的類似,只有在 ScriptableObject 開始時會被呼叫。當遊戲執行(launched)或是有參考到這個 ScriptableObject asset 的 Scene 被載入時會呼叫。
  • OnEnable : 在 Awake 之後呼叫,當 ScriptableObject 被載入(Loaded)或是實體化(Instantiated)時會被呼叫。當 ScriptableObject.CreateInstance 或是 script 被重新編譯(recompilation)時 OnEnable 會被呼叫。
  • OnDisable : 當載入的 Scene 不再參考這個 ScriptableObject asset 時,就會呼叫 OnDisable , 它會在 OnDestory 之前被呼叫。此外在 script 重新編譯進入 Play Mode 時,也會呼叫,此時會出現 OnDisable 出現在 OnEnable 的情況。
  • OnDestory : 當 ScriptableObject 在 編輯器中刪除,或是重程式碼中刪除時,會呼叫 OnDestory 。如果是在執行期間建立的 ScriptableObject 在APP離開或是離開 Play Mode 時也會呼叫。

以下 callback 只在編輯器中呼叫

  • OnValidate : 當值在 Inspector 中改變值,才會呼叫。可以在這邊確保你的編輯器輸入的資料是合適的範圍(如 0 ~ 1)。
  • Reset : 當點擊 Inspector 中的 Reset 按鈕時,會呼叫。

返回 ScriptableObject 系列

Reference:

評論