ScriptableObject as Event

ScriptableObject 可以用來作為事件系統的一部分。這種方法可以幫助我們在不同的 MonoBehaviour 之間進行通信,而不需要它們彼此知道對方的存在,從而實現鬆耦合。

首先要建立 GameEventGameEventListener 。它們兩個互相依賴

GameEvent

  • 含有一個 GameEventListener 列表用來保存要聽取該事件的聽眾,
  • 使用 RegisterListener 來註冊聽眾
  • 使用 UnregisterListener 將聽眾移出
  • 使用 Raise 觸發聽眾註冊的事件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    [CreateAssetMenu(menuName = "ScriptableObjects/GameEvent", order = 1)]
    public class GameEvent : ScriptableObject
    {
    private readonly List<GameEventListener> eventListeners =
    new List<GameEventListener>();

    public void Raise()
    {
    for (int i = eventListeners.Count - 1; i >= 0; i--)
    eventListeners[i].OnEventRaised();
    }

    public void RegisterListener(GameEventListener listener)
    {
    if (!eventListeners.Contains(listener))
    eventListeners.Add(listener);
    }

    public void UnregisterListener(GameEventListener listener)
    {
    if (eventListeners.Contains(listener))
    eventListeners.Remove(listener);
    }
    }

GameEventListener

  • 含有一個要監聽的事件參考 Event
  • 含有一個當監聽到後,要做出的回應 Response
  • OnEnable 中將自己註冊到 Event
  • OnDisable 中將自己從 Event 中移出
  • OnEventRaised 中呼叫回應的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class GameEventListener : MonoBehaviour
{
[Tooltip("Event to register with.")]
public GameEvent Event;

[Tooltip("Response to invoke when Event is raised.")]
public UnityEvent Response;

private void OnEnable()
{
Event.RegisterListener(this);
}

private void OnDisable()
{
Event.UnregisterListener(this);
}

public void OnEventRaised()
{
Response.Invoke();
}
}

接著在 Unity 編輯器中建立一個 ScriptableObject 實體,並命名為 ClickEvent

建立一個 DoSomethingWhenClick 的遊戲物件(GameObject) ,並添加以下腳本,這個遊戲物件有一個公開方法 JustDoIt,當事件發生時,要被呼叫。

1
2
3
4
5
6
7
public class DoSomethingWhenClick : MonoBehaviour
{
public void JustDoIt()
{
Debug.Log("Just Do It");
}
}

建立一個 EventListenerGameObject,將 GameEventListener 腳本添加到這個 EventListener GameObject 中。

  • 接下來將前面建立的 ScriptableObject 實體 ClickEvent ,拖放到,EventListener 中的 Event 欄位,表示這個 EventListener 要監聽的事件是 ClickEvent
  • 然後設定聽到之後要做的事。將 DoSomethingWhenClick 遊戲物件放到 Response 欄位,並設定要執行該物件的公開方法 JustDoIt

接著建立事件發起的物件,這邊建立兩個, RaiseClickableCircleRaiseClickableButWithUnityEvent

RaiseClickableCircle 的腳本如下,它主要是直接使用 GameEvent 作為參考,其缺點是只能用一個 Event

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

public GameEvent ClickGameEvent;

void OnMouseDown()
{
ClickGameEvent.Raise();
}
}

RaiseClickableButWithUnityEvent 幾乎與 RaiseClickableCircle 一模一樣,差別在於是使用 UnityEvent ,這樣可以放置多個 Event,當觸發 Raise 時在裡面的 Event 都會觸發。

1
2
3
4
5
6
7
8
9
public class RaiseClickableButWithUnityEvent : MonoBehaviour
{
public UnityEvent ClickEvent;

void OnMouseDown()
{
ClickEvent.Invoke();
}
}

最後執行,並點擊畫面上的圓形或是方形,可以發現都會觸發 DoSomethingWhenClickJustDoIt 方法,而 RaiseClickableButWithUnityEventRaiseClickableCircleDoSomethingWhenClick 完全不知曉對方的存在。

使用基於事件架構(event-based architecture)的好處是,它不會每一幀都在執行,只有在當事件發生時才會執行,因此效率會比在 MonoBehaviourUpdate 方法中執行好。

返回 ScriptableObject 系列

Reference:

評論