ScriptableObject as Runtime Set

RuntimeSet 的做法是將 ScriptableObject 作為一個分享集合容器,在執行期間,將物件放入,讓其他要使用它的物件可以透過這個分享容器拿到。

以下的例子中,將建立一個 GameObjectRuntimeSet ,這個 GameObjectRuntimeSet 將會在執行時持有 Circle 物件集合,而 Circle 物件將由 CreateObject 建立,而 ObjectMonitor 負責查看 GameObjectRuntimeSet 中有多少 Circle 物件

以下建立一個通用抽象類 RuntimeSet 它繼承 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
public abstract class RuntimeSet<T> : ScriptableObject
{
protected readonly List<T> Items = new();

public int Count => Items.Count;

public void Add(T thing)
{
if (!Items.Contains(thing))
Items.Add(thing);
}

public bool Remove(T thing)
{
return Items.Remove(thing);
}

public T this[int index]
{
get => Items[index];
set => Items[index] = value;
}

public void Clear()
{
Items.Clear();
}
}

接著繼承 RuntimeSet 建立一個 GameObjectRuntimeSet , 這個 GameObjectRuntimeSet 將為 GameObject 的分享集合容器

1
2
3
4
[CreateAssetMenu(menuName = "ScriptableObjects/GameObjectRuntimeSet")]
public class GameObjectRuntimeSet : RuntimeSet<GameObject>
{
}

在 Unity 編輯器中建立一個 ScriptableObject 實體 CircleRuntimeSet

接著建立要放到這個集合的物件 Circle ,在 OnEnable 時將自己加入集合, OnDisable 時將自己從集合中移除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Circle : MonoBehaviour
{
public GameObjectRuntimeSet GameObjectRuntimeSet;
private void OnEnable()
{
GameObjectRuntimeSet.Add(this.gameObject);
}

private void OnDisable()
{
GameObjectRuntimeSet.Remove(this.gameObject);
}

}

ScriptableObject 實體 CircleRuntimeSet 設定到 CircleGameObjectRuntimeSet 中,讓 Circle 知道在 OnEnable 時要放入哪個集合。之後,把 Circle 轉為 Prefab

再建立一個製造CircleCreateObject,這邊建立一個按鈕,當按下時,會觸發 CreateObject.Create()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class CreateObject : MonoBehaviour
{

public GameObject circle;

public void Create()
{
float randomX = Random.Range(-6f, 6f);
float randomY = Random.Range(-6f, 6f);
Vector3 randomPosition = new Vector3(randomX, randomY, 0f);

Instantiate(circle, randomPosition, Quaternion.identity);
}

}

最後建立一個觀看這個集合的物件

1
2
3
4
5
6
7
8
9
10
11
public class ObjectMonitor : MonoBehaviour
{

public GameObjectRuntimeSet GameObjectRuntimeSet;

public void Update()
{
Debug.Log("Current objects in the set " + GameObjectRuntimeSet.Count);
}

}

觀察結果
每按下按鈕一次,所產生的 Circle 物件都會加入 GameObjectRuntimeSet ,而需要知道有多少個 Circle 的物件,它只要從 GameObjectRuntimeSet 裏面就可以找到目前有多少 Circle 物件。

總結

  • RuntimeSetScriptableObject 作為分享的集合容器。
  • 它可以避免使用 Singleton 來管理物件群
  • 它避免了使用 Object.FindObjectOfTypeGameObject.FindWithTag 等開銷可能會比較大的方法來搜尋物件

返回 ScriptableObject 系列

Reference:

評論