Skills/godot-csharp-patterns/SKILL.md

244 lines
5.5 KiB
Markdown

---
name: godot-csharp-patterns
description: "C# Mono patterns for Godot 4.x: Export attributes, signal handling, performance optimization, GDExtension interop, and best practices for first-class C# support. Use when writing C# code in Godot."
license: MIT
compatibility:
- godot-4.0
- godot-4.1
- godot-4.2
- godot-4.3
- godot-4.4
- godot-4.5
- godot-4.6
- godot-4.7
metadata:
author: vl4dt
version: 0.1.0
tags:
- godot
- csharp
- mono
- performance
created: 2026-06-20
---
# C# Patterns for Godot 4.x
Best practices for Godot's first-class C# (Mono) support.
## Export Attributes
### Inspector Properties
```csharp
using Godot;
public partial class Player : CharacterBody2D
{
// Basic export
[Export] public float Speed { get; set; } = 200f;
// With range
[Export(PropertyHint.Range, "0,100,0.5")]
public float JumpForce { get; set; } = 400f;
// File path
[Export(FileFilter = "*.png,*.svg")]
public string IconPath { get; set; } = "";
// Resource export
[Export] public PackedScene BulletScene { get; set; }
// Enum with hint
[Export] public MovementMode MoveMode { get; set; }
public enum MovementMode
{
Walk,
Run,
Sprint
}
}
```
### Export Categories
```csharp
[ExportGroup("Combat")]
[Export] public int AttackDamage { get; set; } = 10;
[Export] public float AttackCooldown { get; set; } = 0.5f;
[ExportGroup("Movement")]
[Export] public float MoveSpeed { get; set; } = 200f;
[Export] public float JumpForce { get; set; } = 400f;
```
## Signal Handling
### C# Event Syntax vs Godot Signals
```csharp
// Godot signal declaration in GDScript (autoload)
// class_name EventBus extends Node
// signal player_died(Vector2 position)
// C# subscriber
public override void _Ready()
{
// Method reference (recommended — auto-disconnect on free)
EventBus.PlayerDied += OnPlayerDied;
// Lambda (requires manual disconnect)
EventBus.PlayerDied += (pos) => GD.Print($"Died at {pos}");
}
public override void _ExitTree()
{
// Clean up lambda subscriptions
EventBus.PlayerDied -= OnPlayerDied;
}
private void OnPlayerDied(Vector2 position)
{
GD.Print($"Player died at {position}");
}
```
### Using Godot.Signal for C#-Side Signals
```csharp
public partial class Enemy : CharacterBody2D
{
[Signal] public delegate void HealthChangedEventHandler(int newHealth);
[Signal] public delegate void DefeatedEventHandler();
private int health = 100;
public void TakeDamage(int amount)
{
health -= amount;
EmitSignal(SignalName.HealthChanged, health);
if (health <= 0)
{
EmitSignal(SignalName.Defeated);
QueueFree();
}
}
}
```
## Performance Considerations
### Avoiding GC Pressure
```csharp
// Bad: Creates garbage every frame
public override void _Process(double delta)
{
var text = $"Score: {score}"; // String allocation
label.Text = text;
}
// Good: Reuse objects, avoid allocations
private readonly StringBuilder _sb = new();
public override void _Process(double delta)
{
_sb.Clear();
_sb.Append("Score: ");
_sb.Append(score);
label.Text = _sb.ToString();
}
```
### Structs vs Classes
```csharp
// Use structs for small, short-lived data
public struct Vector2Int
{
public int X { get; }
public int Y { get; }
public Vector2Int(int x, int y) => (X, Y) = (x, y);
}
// Use classes for objects with identity/lifetime
public class PlayerData : Resource
{
public string Name { get; set; }
public int Level { get; set; }
}
```
### Instance Pooling
```csharp
public partial class BulletPool : Node
{
private readonly Queue<Bullet> _pool = new();
private readonly PackedScene _bulletScene;
public BulletPool(PackedScene bulletScene) => _bulletScene = bulletScene;
public Bullet Get()
{
if (_pool.TryDequeue(out var bullet))
{
bullet.Visible = true;
return bullet;
}
return _bulletScene.Instantiate<Bullet>();
}
public void Return(Bullet bullet)
{
bullet.Visible = false;
bullet.QueueFree();
_pool.Enqueue(bullet);
}
}
```
## GDExtension Interop
### Calling GDScript from C#
```csharp
// GDScript (autoload "GameData")
// class_name GameData extends Node
// var high_score: int = 0
// C# access
var gameData = Engine.GetSingleton("GameData") as GodotObject;
var highScore = gameData.Get("high_score");
```
### Exposing C# to GDScript via GDExtension
Create a `.gdextension` file to expose C# classes to GDScript projects.
## Godot 4.7 C# Notes
- C# fully supports new 4.7 features including VirtualJoystick and tween_await()
- Use `await` with tweens for async animation sequences
- Export attributes work the same — no changes in 4.7
## MCP Bridge Tools (Optional — Live Editor Integration)
When the MCP bridge is running (Phase 2), these tools can inspect and modify your project live:
- **`script_read`** — Read C# script files for review
- **`script_edit`** — Update C# scripts directly
- **`script_validate`** — Validate C# scripts for syntax errors
- **`scene_tree`** — Inspect scene hierarchy to verify node composition
- **`class_db_info`** — Query Godot class info for C# interop patterns
> **Note:** MCP tools require the Godot editor to be running with the MCP plugin enabled. Skills work independently without the bridge.
## References
- [C# API Reference](references/csharp-api.md)
- [Performance Guide](references/performance.md)
- [GDExtension Guide](references/gdextension.md)