Unity 事件入門:5 種 C# 委派與事件用法全面解析

Ted Liou 2025.09.08 Unity
本文深入探討 Unity 遊戲開發中,五種常見的事件(Event)與委派(Delegate)使用方式:基本的 delegate、C# 的 Action 與 EventHandler、以及 Unity 獨有的 UnityAction 與 UnityEvent。透過清晰的程式碼範例與差異比較,幫助開發者快速理解它們的優缺點與最佳應用場景。特別解釋了為何在處理多個參數時,EventHandler 是更優雅且可維護的解決方案。

為什麼 Unity 開發需要事件與委派?

Unity 遊戲開發 中,有效管理物件之間的溝通至關重要。 C# 的事件(Event)與委派(Delegate) 正是實現「解耦合」訊息傳遞的核心工具。

  • 它們允許一個物件在事件觸發時,自動通知 所有監聽者。
  • 事件發送者 不需要知道 接收者的細節,從而降低耦合度。

本文將逐步介紹 五種常見事件處理方式,並透過完整程式碼與比較表格,幫助你判斷在何種情境下該選擇哪一種。

五種事件與委派完整程式碼範例

以下 DelegateExample.cs 腳本展示了五種事件的宣告、訂閱與觸發方式,範例參數統一為:

  • int id
  • GameObject player
  • int score

👉 這樣方便直觀地比較它們的差異。

 1using System;
 2using UnityEngine;
 3using UnityEngine.Events;
 4
 5public class DelegateExample : MonoBehaviour
 6{
 7    // A: 最基本的委派
 8    public delegate void GameEventA(int id, GameObject player, int score);
 9    public event GameEventA OnGameEventA;
10
11    // B: 使用 Action 委派
12    public event Action<int, GameObject, int> OnGameEventB;
13
14    // C: 使用 UnityAction 委派
15    public event UnityAction<int, GameObject, int> OnGameEventC;
16
17    // D: 使用 UnityEvent 委派
18    public UnityEvent<int, GameObject, int> OnGameEventD;
19
20    // E: 使用 EventHandler 委派
21    public class GameEventEArgs : EventArgs
22    {
23        public int id;
24        public GameObject player;
25        public int score;
26
27        public GameEventEArgs(int id, GameObject player, int score)
28        {
29            this.id = id;
30            this.player = player;
31            this.score = score;
32        }
33    }
34    public event EventHandler<GameEventEArgs> OnGameEventE;
35
36    private void Start()
37    {
38        // 取得一個範例用的 GameObject
39        GameObject playerObject = new GameObject("Player");
40
41        // A: 範例
42        OnGameEventA += HandleGameEvent;
43        OnGameEventA += (id, player, score) => Debug.Log($"Event A Triggered. ID: {id}, Player: {player.name}, Score: {score}");
44        OnGameEventA?.Invoke(1, playerObject, 100);
45
46        // B: 範例
47        OnGameEventB += HandleGameEvent;
48        OnGameEventB += (id, player, score) => Debug.Log($"Event B Triggered. ID: {id}, Player: {player.name}, Score: {score}");
49        OnGameEventB?.Invoke(2, playerObject, 200);
50
51        // C: 範例
52        OnGameEventC += HandleGameEvent;
53        OnGameEventC += (id, player, score) => Debug.Log($"Event C Triggered. ID: {id}, Player: {player.name}, Score: {score}");
54        OnGameEventC?.Invoke(3, playerObject, 300);
55
56        // D: 範例
57        OnGameEventD ??= new UnityEvent<int, GameObject, int>();
58        OnGameEventD.AddListener(HandleGameEvent);
59        OnGameEventD.AddListener((id, player, score) => Debug.Log($"Event D Triggered. ID: {id}, Player: {player.name}, Score: {score}"));
60        OnGameEventD.Invoke(4, playerObject, 400);
61
62        // E: 範例
63        OnGameEventE += HandleGameEventE;
64        OnGameEventE += (sender, args) => Debug.Log($"Event E Triggered. ID: {args.id}, Player: {args.player.name}, Score: {args.score}");
65        OnGameEventE?.Invoke(this, new GameEventEArgs(5, playerObject, 500));
66    }
67
68    // 處理委派事件的方法(A, B, C, D 共用)
69    public void HandleGameEvent(int id, GameObject player, int score)
70    {
71        Debug.Log($"Handled Game Event with ID: {id}, Player: {player.name}, Score: {score}");
72    }
73
74    // 處理 EventHandler 事件的方法(E 專用)
75    public void HandleGameEventE(object sender, GameEventEArgs args)
76    {
77        // 注意,這裡的程式碼比 HandleGameEvent 更易於閱讀和維護
78        Debug.Log($"Handled Game Event E with ID: {args.id}, Player: {args.player.name}, Score: {args.score}");
79    }
80}

Unity 中常見的五種事件用法比較

類型優點缺點適用情境
基本 delegate完全自訂義,語法直觀。額外宣告類型,程式碼冗長。需要 高度客製化方法簽名 時。
C# Action簡潔、標準,無需額外宣告。無法在 Unity 編輯器中直接配置。最常用,適合 輕量級事件傳遞
UnityAction與 Action 相似,但 針對 Unity 最佳化與 Action 功能重疊。當需 Unity API 整合 或習慣使用時。
UnityEvent編輯器可見,非程式人員可直接設定。動態註冊語法較繁瑣。適合 UI 按鈕、動畫事件 等由設計師配置的場合。
C# EventHandler標準化,適合多參數,可擴充性強。需額外繼承 EventArgs 類別,語法稍繁瑣。大型專案、參數多且可能擴充 的事件。

EventHandler:多參數事件的最佳解法

在範例中,所有事件都傳遞了三個參數。但對比 HandleGameEventHandleGameEventE,你會發現 EventHandler 的優勢

  1. 程式碼可讀性更佳

    • Action:必須記住參數順序與意義,維護成本高。
    • EventHandler:所有資料封裝在 args,存取直觀 (args.idargs.player)。
  2. 避免參數順序錯誤

    • Action<int, GameObject, int> 很容易傳錯順序。
    • EventHandler 只傳 EventArgs 物件,完全避免此問題。
  3. 高度可擴充性

    • Action:新增參數需修改所有訂閱方法。
    • EventHandler:只需在繼承 EventArgs 的類別中新增屬性,不影響既有訂閱者 (其他程式碼)。

結論:如何選擇適合的事件類型?

  • 小型專案、簡單事件 → 優先使用 ActionUnityAction,直觀又高效。
  • 需要編輯器配置 → 選擇 UnityEvent,方便設計師與企劃人員。
  • 多參數、可擴充性 → 推薦使用 EventHandler,更專業、健壯、好維護。

目前我在業界中,在無參數或少參數的需求下,Action 是 C# 與 Unity 專案的最佳首選,寫出來的程式碼可以任意的移植到 Unity 或非 Unity 的 C# 專案中 (只有 Unity 的話也能用 UnityAction)。當參數較多的時候,則改用 EventHandler 的方式來進行,可以維持良好的可讀性、擴展與維護性。

UnityEvent 在程式設計上的優點,是它在呼叫前不需要ActionEventHandler 那樣進行 null 檢查,但其動態註冊的語法較為冗長

Ted Liou

我是一位遊戲開發工程師 / 軟體工程師,專精於 Unity 和 C#。我善於自學新技術,並樂於將所學轉化為淺顯易懂的教材,幫助他人學習與成長。