Quick answer: Name delegate with EventHandler suffix. Emit via EmitSignal(SignalName.HealthChanged, value). Connect via C# event syntax.
You write EmitSignal("health_changed", 50) by hand. Method connected to HealthChanged never runs because the literal name doesn’t match the generated signal.
The Symptom
EmitSignal returns; receiver method does not. No error.
The Fix
public partial class Player : Node {
[Signal]
public delegate void HealthChangedEventHandler(int newHealth);
public void TakeDamage(int amount) {
Health -= amount;
EmitSignal(SignalName.HealthChanged, Health);
}
}
public partial class UI : Control {
[Export] public Player Player;
public override void _Ready() {
Player.HealthChanged += OnHealthChanged;
}
private void OnHealthChanged(int hp) {
bar.Value = hp;
}
}
Source generator strips EventHandler from delegate name to produce signal HealthChanged. SignalName.HealthChanged is the type-safe constant used at emit time.
Verifying
Take damage. UI bar updates. Without the EventHandler suffix: signal isn’t generated, EmitSignal silently fails.
“EventHandler suffix. SignalName constant. Connection holds.”
Related Issues
For C# Export flags, see flags. For C# List property, see List.
Suffix matters. Constants safe. Signal lands.