Use when working with 2D-specific systems — TileMaps, parallax scrolling, 2D lights and shadows, canvas layers, particles 2D, custom drawing, and 2D meshes in Godot 4.3+
中文简介正在准备中。当前显示原文说明。
Use when implementing signals in C# — [Signal] delegates, EmitSignal patterns, async signal awaiting, and event-driven architecture. Triggers on declaring, emitting, connecting, or awaiting a signal from a C# script, and on a signal crossing between C# and GDScript in either direction.
中文简介正在准备中。当前显示原文说明。
查看安装方法安装前可以查看交给 AI 代理的指令内容。
This skill is C# only. For general C# conventions and project setup, see the csharp-godot skill. Godot signals in C# require a different mental model from GDScript: delegates declared with [Signal], strongly-typed +=/-= connections, and mandatory disconnection in _ExitTree(). All examples target Godot 4.x with no deprecated APIs.
Related skills: csharp-godot for C# conventions and project setup, event-bus for global signal hub architecture, component-system for signal-based component communication.
Signals are declared as public delegate void with the [Signal] attribute inside a partial class that extends a Godot type. The delegate name must end with EventHandler — Godot strips that suffix to produce the signal name exposed to the engine.
using Godot;
public partial class Player : CharacterBody2D
{
// Signal name in engine: "HealthChanged"
[Signal] public delegate void HealthChangedEventHandler(int current, int maximum);
// Signal name in engine: "Died"
[Signal] public delegate void DiedEventHandler();
// Signal name in engine: "ItemCollected"
[Signal] public delegate void ItemCollectedEventHandler(string itemName);
}
Naming rules:
| Delegate name | Engine signal name |
|---|---|
HealthChangedEventHandler | HealthChanged |
DiedEventHandler | Died |
ItemCollectedEventHandler | ItemCollected |
PlayerSpawnedEventHandler | PlayerSpawned |
Omitting the EventHandler suffix is a compile error: the source generator reports GD0201, "The name of the delegate must end with 'EventHandler'".
Parameter type constraints: Signal parameters must be Variant-compatible: numeric types, bool, string, enums, Godot structs (Vector2, Color, …), StringName, NodePath, GodotObject subclasses, Godot.Collections.Array/Dictionary (including the generic Array<T>/Dictionary<TKey, TValue>) and packed-array equivalents such as int[]. Other C# classes and structs fail the build with GD0202.
Use EmitSignal(SignalName.SignalName, args...). The SignalName nested class is auto-generated by the Godot source generators at build time — one cached StringName per declared signal.
using Godot;
public partial class Player : CharacterBody2D
{
[Signal] public delegate void HealthChangedEventHandler(int current, int maximum);
[Signal] public delegate void DiedEventHandler();
[Signal] public delegate void ItemCollectedEventHandler(string itemName);
[Export] public int MaxHealth { get; set; } = 100;
private int _currentHealth;
public override void _Ready()
{
_currentHealth = MaxHealth;
}
public void TakeDamage(int amount)
{
_currentHealth = Mathf.Clamp(_currentHealth - amount, 0, MaxHealth);
// Type-safe emission — SignalName.HealthChanged is a generated constant.
EmitSignal(SignalName.HealthChanged, _currentHealth, MaxHealth);
if (_currentHealth == 0)
EmitSignal(SignalName.Died);
}
public void CollectItem(string itemName)
{
EmitSignal(SignalName.ItemCollected, itemName);
}
}
EmitSignal does not check its arguments against the delegate. With the wrong argument count, += handlers on a custom [Signal] are skipped without an error, and Connect/GDScript receivers log Error calling from signal … Method expected N argument(s), but called with M. With a wrong type, C# handlers get the value converted or defaulted (0, null); a typed GDScript receiver logs Cannot convert argument N from X to Y and is not called.
+= operator (preferred)using Godot;
public partial class HudLayer : CanvasLayer
{
private Player _player;
public override void _Ready()
{
_player = GetNode<Player>("../Player");
// Connect with += — mirrors C# event syntax.
_player.HealthChanged += OnHealthChanged;
_player.Died += OnDied;
_player.ItemCollected += OnItemCollected;
}
private void OnHealthChanged(int current, int maximum)
{
GetNode<ProgressBar>("HealthBar").Value = (double)current / maximum * 100.0;
GetNode<Label>("HealthLabel").Text = $"{current} / {maximum}";
}
private void OnDied()
{
GetNode<Control>("DeathScreen").Show();
}
private void OnItemCollected(string itemName)
{
GetNode<Label>("PickupLabel").Text = $"Picked up: {itemName}";
}
}
Use lambdas for one-off, short-lived responses. Store the lambda in a field if you need to disconnect it later.
// Anonymous lambda — cannot be disconnected by reference later.
_player.Died += () => GetNode<AudioStreamPlayer>("DeathSound").Play();
// Stored lambda — can be disconnected.
private Action<string> _onItemCollected;
public override void _Ready()
{
_onItemCollected = (itemName) =>
{
_collectCount++;
UpdateCollectDisplay();
};
_player.ItemCollected += _onItemCollected;
}
public override void _ExitTree()
{
_player.ItemCollected -= _onItemCollected;
}
_Ready()Connect inside _Ready() — or _EnterTree() for a node that is removed and re-added, since _Ready() runs only once. The node's references are resolved and the scene tree is available at that point. Connecting in the constructor or field initializers may fail because Godot node infrastructure is not yet initialised.
When a GodotObject is freed, Godot disconnects its connections, for emitters and receivers alike. Two cases are not cleaned up when the receiver is freed while the emitter lives on: a += on a custom [Signal], and a lambda that captures a variable. In both, the next emission reaches a disposed object and throws ObjectDisposedException, so disconnect with -= yourself in _ExitTree(). For a node that can leave and re-enter the tree, connect in _EnterTree() instead of _Ready(), which runs only once.
See references/disconnecting.md for the
-=cleanup pattern, the two cases Godot does not clean up, and a SafeDisconnect helper.
await ToSignal(node, SignalName.X) pauses until the signal fires. Returns Variant[] of the signal's args. Add a timeout with Task.WhenAny or a CancellationTokenSource to avoid permanent hangs.
See references/awaiting-signals.md for basic await, return-value extraction, and both timeout patterns.
Three patterns: typed event args (wrap signal payload in a Resource subclass), static event bus (use C# static event as a pure-C# alternative when both peers are C#), generic signal helper (one helper per SignalName.X to reduce repetition).
See references/custom-signal-patterns.md for full code on each pattern.
When the signal is declared in a GDScript node and the listener is C#, use node.Connect("signal_name", new Callable(this, MethodName.Handler)). The MethodName generated symbol still works for C# methods.
See references/connecting-gdscript-signals.md for the full pattern with both string-name and
MethodNamevariants.
| Mistake | Symptom | Fix |
|---|---|---|
Forgetting EventHandler suffix on the delegate | Build fails with GD0201: the delegate name must end with 'EventHandler' | Rename to XxxEventHandler |
Wrong parameter types in EmitSignal | C# handlers: no error, the value is converted or defaulted (0, null); typed GDScript receivers: Cannot convert argument N from X to Y, handler not called | Match EmitSignal args exactly to the delegate signature |
| Connecting to a freed object | ObjectDisposedException or silent crash on next signal emission | Guard with IsInstanceValid before connecting; store reference safely |
Not disconnecting a custom [Signal] or a capturing lambda | ObjectDisposedException on the next emission after the receiver is freed; built-in signals with a method handler are cleaned up automatically | Add a matching -= in _ExitTree() (or Dispose()) for those two cases |
Passing wrong argument count to EmitSignal | += handlers silently not called; Connected receivers log "Method expected N argument(s), but called with M" | Count signal delegate parameters and match exactly |
| Using static C# events for cross-language signals | GDScript cannot observe the event; no error, just silent non-delivery | Use Godot [Signal] for any signal GDScript must receive |
| Double-connecting the same handler | On a custom [Signal] the handler fires twice per emission; on a built-in signal the second += logs "already connected" | Check if already connected, or use ConnectFlags.OneShot; never call += twice for the same method |
Awaiting a signal in _Ready() before the tree is ready | NullReferenceException or signal never resolves because the source node is not yet in the tree | Defer with await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame) first, or move the await to a method called after _Ready() |
[Signal] delegate name ends with EventHandlerEmitSignal uses SignalName.XxxName constants (not raw strings)EmitSignal argument count and types match the delegate signature exactly+= connections are in _Ready() (not in constructors or field initializers)+= on a custom [Signal], and every capturing lambda, has a matching -=IsInstanceValid guards are in place where the signal source may be freed before the subscriberasync signal awaits are not blocking _Ready() directly; deferred if necessaryConnect("snake_case_name", Callable.From<T>(Method))RefCounted-derived wrapper classes used for complex signal payloads, not plain C# classes or structs还没有评价。欢迎分享你的使用感受。
无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。
还没有评论。来写第一条吧。
Use when working with 2D-specific systems — TileMaps, parallax scrolling, 2D lights and shadows, canvas layers, particles 2D, custom drawing, and 2D meshes in Godot 4.3+
中文简介正在准备中。当前显示原文说明。
Use when working with 3D-specific systems — materials, lighting, shadows, environment, global illumination, fog, LOD, occlusion culling, and decals in Godot 4.3+
中文简介正在准备中。当前显示原文说明。
Use when building character abilities — Resource-based abilities with cost/cooldown/cast, buffs/debuffs, stat modifiers, gameplay tags, and HUD binding
中文简介正在准备中。当前显示原文说明。
Use when creating Godot editor plugins — EditorPlugin, @tool scripts, custom inspectors, and dock panels
中文简介正在准备中。当前显示原文说明。
Use when implementing AI movement — NavigationAgent2D/3D, steering behaviors, behavior trees, and patrol patterns
中文简介正在准备中。当前显示原文说明。
Use when implementing animations — AnimationPlayer, AnimationTree, blend trees, state machines, sprite animation, and code-driven animation
中文简介正在准备中。当前显示原文说明。