前言 在 Godot 中,信号系统(Signals) 是实现节点间通信的核心机制,它本质上是 Godot 引擎内置的、高度优化的“事件”系统。
与 C# 原生的 delegate/event 不同,Godot 信号跨语言(GDScript/C#/VisualScript)、支持编辑器可视化连接,并能自动处理生命周期(如节点释放时自动断开连接)。
因此在 Godot 项目中应优先使用信号而非手动实现 C# event 。
Godot 信号的三种使用方式
连接引擎内置信号 (如 Area2D.area_entered)
自定义信号并发射
通过编辑器连接信号
示例场景:玩家进入区域 → 触发宝箱开启
我们将创建:
一个 Player(CharacterBody2D)
一个 TreasureChest(Area2D + 自定义信号)
下面以 C# 脚本 为例,完整演示如何在 Godot 4.x 中使用信号系统。
为宝箱定义自定义信号 为宝箱定义自定义信号(C#)
信号的定义必须以EventHandler结束,会自动生成前面名称的信号,比如下面示例的SignalName.ChestOpened
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 using Godot;public partial class TreasureChest : Area2D { [Signal ] public delegate void ChestOpenedEventHandler () ; public override void _Ready() { AreaEntered += OnAreaEntered; } private void OnAreaEntered (Area2D area ) { if (area.IsInGroup("player" )) { GD.Print("玩家靠近宝箱!" ); EmitSignal(SignalName.ChestOpened); Monitoring = false ; } } }
关键点:
使用 [Signal] 特性声明信号。
信号名会自动生成 SignalName.ChestOpened(字符串安全)。
用 EmitSignal() 发射信号。
玩家脚本 1 2 3 4 5 6 7 8 9 10 11 12 13 using Godot;public partial class Player : CharacterBody2D { public override void _Ready() { AddToGroup("player" ); } }
连接信号两种方式 在 C# 中动态连接 推荐用于运行时逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public partial class LevelManager : Node2D { private TreasureChest chest; private UIController ui; public override void _Ready() { chest = GetNode<TreasureChest>("TreasureChest" ); ui = GetNode<UIController>("UI" ); chest.Connect(TreasureChest.SignalName.ChestOpened, Callable.From(ui.OnChestOpened)); } } public partial class UIController : Control { public void OnChestOpened () { GD.Print("UI:宝箱已开启!播放动画/加金币..." ); } }
✅ 优点:灵活,适合动态生成的对象。
在 Godot 编辑器中连接 适合静态场景
选中 TreasureChest 节点;
在右侧 “Node” 面板 → “Signals” 标签;
找到 ChestOpened 信号,双击;
选择目标节点(如 UIController);
输入方法名(如 OnChestOpened),点击“连接”。
Godot 会自动生成对应方法(如果不存在)。
断开信号(避免内存泄漏) 当节点被释放时,Godot 会自动断开其所有信号连接 ,所以通常无需手动断开。
但如果你在代码中跨场景或长期持有引用 ,建议显式断开:
1 2 chest.Disconnect(TreasureChest.SignalName.ChestOpened,Callable.From(ui.OnChestOpened));
重要提示
事项
说明
信号 vs C# event
优先用 Godot 信号!它与引擎深度集成,支持编辑器、序列化、跨语言。
参数传递
信号可带参数(最多 9 个),在 [Signal] 委托中定义即可。
性能
信号调用比直接方法调用稍慢,但对游戏逻辑完全够用。
类型安全
C# 中使用 SignalName.XXX 和 Callable.From() 可获得编译时检查。
带参数的信号示例 1 2 3 4 5 6 7 8 9 10 11 [Signal ] public delegate void ItemCollectedEventHandler (string itemName, int count ) ;EmitSignal(SignalName.ItemCollected, "金币" , 10 ); public void OnItemCollected (string name, int count ){ GD.Print($"获得 {count} 个 {name} " ); }
绑定
1 2 3 4 5 6 7 8 9 10 11 12 13 private TouchArea touchArea;public override void _Ready(){ touchArea = GetNode<TouchArea>("Area2D" ); touchArea.Connect(TouchArea.SignalName.ItemCollected, new Callable(this , nameof (OnItemCollected))); } public void OnItemCollected (string name, int count ){ GD.Print($"获得 {count} 个 {name} " ); }
总结 Godot 信号使用流程 :
用 [Signal] 声明信号;
用 EmitSignal() 发射;
用 Connect() 或编辑器连接;
在目标方法中响应。
这是 Godot 官方推荐的节点通信方式,简洁、安全、高效。