npx skills add ...
npx skills add gamedev-skills/awesome-gamedev-agent-skills --skill godot-csharp
Use C#/.NET in Godot 4.7: partial classes extending nodes, the PascalCase lifecycle (_Ready/_Process/_PhysicsProcess), [Export] fields, [Signal] delegates as C# events, type-safe node lookup, and calling between C# and GDScript. Use when writing Godot game code in C# (.cs files, .csproj), needing the Godot .NET build, converting GDScript patterns to C#, or wiring Godot signals as C# events.
npx skills add gamedev-skills/awesome-gamedev-agent-skills --skill godot-csharp
Write Godot game code in C#: node subclasses, the engine lifecycle, exports, signals as events, and GDScript interop. Targets Godot 4.7 (.NET / C#) and .NET 8.
.cs + .csproj), translating GDScript idioms
to C#, exposing [Export] fields, or wiring [Signal] delegates and GetNode.When not to use: GDScript-specific syntax → godot-gdscript; engine concepts that
are language-neutral (scenes, physics, animation) → the relevant godot-* skill. You need
the Godot .NET build + the .NET SDK installed; the standard build can't run C#.
.csproj/.sln. Build with the editor or dotnet build.partial class extending a Godot type (the source generator
relies on partial). The file/class name should match the node script.double delta: _Ready(),
_Process(double delta), _PhysicsProcess(double delta).[Export]; they show in the Inspector like GDScript @export.[Signal] delegates named XxxEventHandler; emit with
EmitSignal(SignalName.Xxx, ...) and subscribe with the generated C# event.GetNode<T>("Path") (or %Unique), and call into GDScript with
Call/Get/Set when needed.partial. Without partial, the Godot source generator can't extend the
class and [Export]/[Signal] break with confusing build errors._Ready, _Process(double),
_PhysicsProcess(double) — PascalCase and double delta (GDScript uses snake_case and
float). A mismatched name just won't be called.[Signal] delegate naming. It must end with EventHandler; the engine exposes the
signal as the name without that suffix and generates SignalName.X and a C# event.GD.Print vs Console.WriteLine. Use GD.Print/GD.PrintErr to reach the Godot
output panel; Console output may not appear.Vector2, Color, Transform2D are structs — mutate a local
copy (var v = Velocity; v.X = ...; Velocity = v;); editing Velocity.X directly won't
compile/persist.QueueFree() vs Free() — same rules as GDScript; prefer QueueFree(). Disposed
objects throw ObjectDisposedException if used after freeing.[ExportGroup], ranges, typed arrays), async with
await ToSignal(...), Godot.Collections vs System collections, custom Resources in
C#, and project/build setup, read references/csharp-setup-and-interop.md.godot-gdscript — the GDScript equivalents of these patterns.godot-signals-groups — signal/event architecture (language-neutral).godot-resources — data resources; the C# [Export] + Resource pattern.unity-csharp-scripting — C# in Unity, for developers coming from there.*using Godot;
public partial class Health : Node
{
// Delegate name MUST end with "EventHandler"; generator creates the event + SignalName.
[Signal] public delegate void HealthChangedEventHandler(int current, int max);
private int _hp = 100;
public void TakeDamage(int amount)
{
_hp = Mathf.Max(_hp - amount, 0);
EmitSignal(SignalName.HealthChanged, _hp, 100); // type-safe signal name
}
public override void _Ready()
{
HealthChanged += OnHealthChanged; // subscribe like a normal C# event
}
private void OnHealthChanged(int current, int max) => GD.Print($"HP {current}/{max}");
}public partial class Spawner : Node2D
{
// Load once; PackedScene is the C# equivalent of preload's result.
private readonly PackedScene _bullet = GD.Load<PackedScene>("res://bullet.tscn");
public void Shoot(Vector2 at)
{
var b = _bullet.Instantiate<Node2D>(); // typed instantiate
b.GlobalPosition = at;
AddChild(b);
}
}public override void _Ready()
{
Node gd = GetNode("GDScriptNode");
// Call a GDScript method and read/write its properties dynamically.
gd.Call("take_damage", 10);
int score = (int)gd.Get("score");
gd.Set("score", score + 5);
// Connect to a GDScript signal by name:
gd.Connect("died", Callable.From(OnDied));
}
private void OnDied() => GD.Print("entity died");