--- name: godot-setup-csharp version: 3.0.0 displayName: Setup C# Integration description: > Use when adding C# support to Godot 4.x projects or creating mixed GDScript/C# architectures. Sets up project structure, configures .csproj files, enables GDScript to C# communication, implements signal connections in C#, handles resource loading, and identifies performance-critical code candidates for C# optimization. author: Asreonn license: MIT category: game-development type: tool difficulty: intermediate audience: [developers] keywords: - godot - csharp - dotnet - csproj - gdscript-interop - signals - resource-loading - performance - mono - mixed-language platforms: [macos, linux, windows] repository: https://github.com/asreonn/godot-superpowers homepage: https://github.com/asreonn/godot-superpowers#readme permissions: filesystem: read: [".gd", ".cs", ".csproj", ".sln"] write: [".cs", ".csproj", ".sln"] git: true behavior: auto_rollback: true validation: true git_commits: true outputs: "C# project files, .csproj configuration, C# script templates, signal bindings, resource loading code" requirements: "Godot 4.x with .NET support, dotnet CLI, Git repository" execution: "Semi-automatic with manual review points" integration: "Works with godot-refactor, godot-extract-resources, godot-add-signals" --- # Setup C# Integration ## Core Principle **Use C# for performance-critical code, GDScript for rapid iteration.** Godot 4.x's .NET integration enables seamless mixed-language development. ## What This Skill Does Transforms single-language projects into optimized mixed GDScript/C# architectures: 1. **Generates project structure** - .csproj, .sln, solution folders 2. **Enables cross-language communication** - GDScript calling C#, C# calling GDScript 3. **Implements signals in C#** - Type-safe signal connections and emissions 4. **Sets up resource loading** - Loading .tres, .tscn files from C# 5. **Identifies optimization candidates** - Code patterns that benefit from C# ## Detection Patterns Identifies candidates for C# migration: - Heavy mathematical operations (physics, pathfinding, AI) - Complex data processing (inventory systems, save games) - External library needs (NuGet packages, .NET ecosystem) - Performance bottlenecks in GDScript profiling - Systems requiring strong typing (networking, serialization) ## When to Use ### You're Starting a New Godot 4.x Project Set up C# from the beginning for mixed-language flexibility. ### You Have Performance Bottlenecks GDScript profiler shows heavy computation in hot paths. ### You Need .NET Libraries Want to use Newtonsoft.Json, System.Text.Json, Math.NET, etc. ### You're Porting from Unity Existing C# codebase to integrate with Godot. ### You Want Strong Typing Large codebase where compile-time type checking prevents bugs. ## Process 1. **Analyze** - Profile GDScript, identify C# candidates 2. **Generate** - Create .csproj with Godot.NET.Sdk 3. **Configure** - Set up solution structure and references 4. **Migrate** - Convert selected scripts to C# 5. **Interop** - Set up GDScript ↔ C# communication 6. **Signals** - Implement signal connections in C# 7. **Resources** - Configure resource loading from C# 8. **Validate** - Test cross-language functionality 9. **Commit** - Git commit per major component ## Project Structure Generated ``` project-root/ ├── project.godot ├── MyProject.csproj # Auto-generated ├── MyProject.sln # Solution file ├── scripts/ │ ├── gdscript/ # GDScript files │ └── csharp/ # C# scripts │ ├── Core/ # Performance-critical systems │ ├── Utils/ # Helper classes │ └── Resources/ # Resource loaders └── .vscode/ └── launch.json # Debug configuration ``` ## Example Transformations ### Project Setup **Before (GDScript only):** ```gdscript # No C# configuration ``` **After (Mixed setup):** ```xml net8.0 true ``` ### GDScript Calling C# **C# Class:** ```csharp // scripts/csharp/Pathfinder.cs using Godot; namespace MyProject.Core; [GlobalClass] public partial class Pathfinder : Node { [Export] public float CellSize { get; set; } = 32.0f; public Godot.Collections.Array FindPath(Vector2 start, Vector2 end) { // A* implementation in C# var path = new Godot.Collections.Array(); // ... pathfinding logic return path; } } ``` **GDScript Usage:** ```gdscript # enemy.gd @onready var pathfinder: Pathfinder = $"../Pathfinder" func move_to_target(target_pos: Vector2): var path = pathfinder.find_path(global_position, target_pos) follow_path(path) ``` ### C# Calling GDScript **GDScript with Methods:** ```gdscript # ui_manager.gd class_name UIManager func show_damage_number(amount: int, position: Vector2): var label = preload("res://ui/damage_label.tscn").instantiate() label.text = str(amount) label.position = position add_child(label) ``` **C# Calling GDScript:** ```csharp // scripts/csharp/CombatSystem.cs using Godot; public partial class CombatSystem : Node { private Node _uiManager; public override void _Ready() { _uiManager = GetNode("../UIManager"); } public void ApplyDamage(Node target, int damage) { // Call GDScript method from C# var pos = ((Node2D)target).GlobalPosition; _uiManager.Call("show_damage_number", damage, pos); } } ``` ### Signals in C# **Defining and Emitting:** ```csharp // scripts/csharp/HealthComponent.cs using Godot; namespace MyProject.Core; [GlobalClass] public partial class HealthComponent : Node { [Signal] public delegate void HealthChangedEventHandler(int newHealth, int maxHealth); [Signal] public delegate void DiedEventHandler(); [Export] public int MaxHealth { get; set; } = 100; private int _currentHealth; public int CurrentHealth { get => _currentHealth; set { _currentHealth = Mathf.Clamp(value, 0, MaxHealth); EmitSignal(SignalName.HealthChanged, _currentHealth, MaxHealth); if (_currentHealth <= 0) EmitSignal(SignalName.Died); } } } ``` **Connecting in GDScript:** ```gdscript # player.gd @onready var health: HealthComponent = $HealthComponent func _ready(): health.health_changed.connect(_on_health_changed) health.died.connect(_on_died) func _on_health_changed(new_health: int, max_health: int): update_health_bar(new_health, max_health) func _on_died(): play_death_animation() ``` **Connecting in C#:** ```csharp // scripts/csharp/HealthBarUI.cs using Godot; public partial class HealthBarUI : ProgressBar { public override void _Ready() { var healthComponent = GetNode("../HealthComponent"); healthComponent.HealthChanged += OnHealthChanged; } private void OnHealthChanged(int newHealth, int maxHealth) { MaxValue = maxHealth; Value = newHealth; } } ``` ### Resource Loading in C# **Loading Custom Resources:** ```csharp // scripts/csharp/WeaponLoader.cs using Godot; namespace MyProject.Utils; [GlobalClass] public partial class WeaponLoader : Node { public WeaponData LoadWeapon(string weaponId) { string path = $"res://resources/weapons/{weaponId}.tres"; return GD.Load(path); } public PackedScene LoadScene(string scenePath) { return GD.Load(scenePath); } } // Custom resource class [GlobalClass] public partial class WeaponData : Resource { [Export] public string WeaponName { get; set; } [Export] public int Damage { get; set; } [Export] public float AttackSpeed { get; set; } [Export] public Texture2D Icon { get; set; } } ``` **GDScript Resource Definition:** ```gdscript # resources/weapon_data.gd class_name WeaponData extends Resource @export var weapon_name: String @export var damage: int @export var attack_speed: float @export var icon: Texture2D ``` ## Performance-Critical Code Identification ### Migrate to C# When You See: | Pattern | GDScript Cost | C# Benefit | |---------|--------------|------------| | Heavy loops (1000+ iterations) | Interpreted overhead | Compiled, JIT-optimized | | Complex math operations | Slower float math | Vectorized operations | | String manipulation | Allocations | StringBuilder, spans | | Dictionary lookups | Hash overhead | Generic Dictionary | | JSON serialization | Manual parsing | Newtonsoft.Json/System.Text.Json | | Physics calculations | Per-frame interpreted | Native performance | | Pathfinding/AI | Algorithm overhead | A* in C#, multithreading | ### Examples of Good C# Candidates: ```gdscript # BAD: Heavy computation in GDScript func process_voxels(): for x in range(64): for y in range(64): for z in range(64): var voxel = calculate_voxel(x, y, z) # 262k iterations! # BETTER: Call C# for heavy work @onready var voxel_processor: VoxelProcessor = $VoxelProcessor func process_voxels(): var result = voxel_processor.process_chunk(position) # C# handles loops ``` ```csharp // C# Implementation using Godot; public partial class VoxelProcessor : Node { public byte[,,] ProcessChunk(Vector3I chunkPos) { var voxels = new byte[64, 64, 64]; // Fast C# loops with compiler optimizations for (int x = 0; x < 64; x++) for (int y = 0; y < 64; y++) for (int z = 0; z < 64; z++) voxels[x, y, z] = CalculateVoxel(chunkPos, x, y, z); return voxels; } } ``` ## Communication Patterns ### Pattern 1: C# Core with GDScript UI - C#: Game logic, AI, physics, data processing - GDScript: UI, animation, scene management, input handling ### Pattern 2: GDScript Gameplay with C# Utilities - GDScript: Main gameplay loops, node interactions - C#: Math utilities, save/load systems, external API calls ### Pattern 3: Shared Resources - Both languages use same .tres resource files - C# Resource classes with [GlobalClass] attribute - GDScript resource definitions ## What Gets Created - `.csproj` project file with Godot.NET.Sdk - `.sln` solution file for IDE integration - C# script templates in `scripts/csharp/` - `[GlobalClass]` attributed classes for cross-language use - Signal definitions with proper C# syntax - Resource loader utilities - `.vscode/launch.json` for debugging - Documentation of interop boundaries ## Safety - Gradual migration - one system at a time - Git commits at each integration point - Validation tests for cross-language calls - Type safety prevents runtime errors - Original GDScript preserved in git history ## When NOT to Use Don't add C# if: - Team has no C# experience - Project is small (< 1000 lines) - No performance issues exist - Build complexity isn't worth benefit - Target platform doesn't support .NET (some consoles) ## Best Practices ### Keep Interop Boundaries Clean ```csharp // GOOD: Self-contained C# class public partial class InventorySystem : Node { public void AddItem(string itemId, int quantity) { } public bool RemoveItem(string itemId, int quantity) => true; public Godot.Collections.Array GetItems() => null; } ``` ### Use Godot Types for Interop ```csharp // GOOD: Godot types work across languages public void MoveEntity(Node2D entity, Vector2 position) { } // AVOID: Pure C# types require conversion public void ProcessList(List items) { } // GDScript can't call easily ``` ### Signal Naming Consistency ```csharp // C# signals use PascalCase [Signal] public delegate void ItemCollectedEventHandler(string itemId); // Connect from GDScript (snake_case in GDScript) inventory_system.item_collected.connect(_on_item_collected) ``` ### Export Properties Work Both Ways ```csharp // C# exports visible in editor [Export] public float MovementSpeed { get; set; } = 200.0f; [Export] public PackedScene ProjectileScene { get; set; } ``` ## Integration Works with: - **godot-extract-resources** - C# Resource classes with [GlobalClass] - **godot-add-signals** - Type-safe C# signal implementations - **godot-refactor** (orchestrator) - Mixed-language architecture decisions ## Common Transformations | Scenario | GDScript | C# | |----------|----------|-----| | Class declaration | `class_name MyClass` | `public partial class MyClass : Node` | | Export variable | `@export var speed: float` | `[Export] public float Speed { get; set; }` | | Signal | `signal health_changed` | `[Signal] public delegate void HealthChangedEventHandler(int health);` | | Method call to GDScript | `node.method()` | `node.Call("method", args)` | | Load resource | `load("path")` | `GD.Load("path")` | | Dictionary | `var dict = {}` | `new Godot.Collections.Dictionary()` | | Array | `var arr = []` | `new Godot.Collections.Array()` | ## Debugging Setup ```json // .vscode/launch.json { "version": "0.2.0", "configurations": [ { "name": "Play", "type": "coreclr", "request": "launch", "program": "${config:godotEditorPath}", "args": ["--path", "${workspaceRoot}"], "cwd": "${workspaceRoot}", "stopAtEntry": false } ] } ``` ## Benefits - **Performance** - Compiled C# runs 10-100x faster for heavy computation - **Type Safety** - Compile-time error detection - **Ecosystem** - Access to NuGet packages and .NET libraries - **Tooling** - Visual Studio, VS Code, Rider with full IDE support - **Debugging** - Full debugger support with breakpoints - **Gradual** - Migrate systems incrementally