前言
在 Godot 中,引擎已提供信号系统(Signals),不需要自己写 delegate/event。
但是
通过信号的话方法的连接没法校验参数,如果脚本也只考虑全局都使用C#,可以通过delegate/event就可以解决这个问题。
在 C# 中,delegate(委托)和 event(事件)是实现 发布-订阅模式(观察者模式)的核心机制。
它们常用于解耦代码,比如 UI 响应、游戏逻辑通知、异步回调等。
下面通过一个 简单而完整的示例 来说明 delegate + event 的使用方式。
场景:玩家生命值变化时触发“受伤”或“死亡”事件
我们将创建:
- 一个
Player 类,内部定义事件;
- 一个
UIManager 类,订阅该事件并做出响应。
发布者
定义委托(Delegate)
1 2
| public delegate void HealthChangedHandler(float currentHealth);
|
也可以使用内置泛型委托如 Action<float>,但自定义委托语义更清晰(尤其在需要多个参数或文档说明时)。
在发布者类中声明事件(Event)
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 System;
public class Player { public event HealthChangedandler HealthChanged;
private float health = 100f; public float Health { get => health; set { health = Math.Max(0, value); OnHealthChanged(); } }
protected virtual void OnHealthChanged() { HealthChanged?.Invoke(health); }
public void TakeDamage(float damage) { Health -= damage; if (health <= 0) { GD.Print("玩家死亡!"); } } }
|
💡 注意:event 关键字限制了外部只能 += 或 -= 订阅/取消订阅,不能直接调用或赋值(如 HealthChanged = null 是非法的),保证了封装性。
订阅者监听事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class UIManager { public void Subscribe(Player player) { player.HealthChanged += OnPlayerHealthChanged; }
public void Unsubscribe(Player player) { player.HealthChanged -= OnPlayerHealthChanged; }
private void OnPlayerHealthChanged(float currentHealth) { Console.WriteLine($"【UI】玩家当前生命值: {currentHealth}"); } }
|
使用示例(主程序)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Program { static void Main() { var player = new Player(); var ui = new UIManager();
ui.Subscribe(player);
player.TakeDamage(20); player.TakeDamage(90);
ui.Unsubscribe(player); } }
|
使用 Action<T> 简化(可选)
如果你不想自定义委托,可以用内置的 Action:
1 2 3 4 5 6
| public class Player { public event Action<float> HealthChanged;
}
|
这样更简洁,适合简单场景。
总结
| 概念 |
作用 |
delegate |
定义方法签名(函数类型) |
event |
基于委托的特殊字段,提供发布-订阅机制 |
+= / -= |
订阅 / 取消订阅事件 |
?.Invoke() |
安全触发事件(防止空引用) |