Keep Your C#: Moving from Unity to Godot without GDScript

Keep writing C# after leaving Unity: set up .NET for Godot, learn the PascalCase API shift, use [Export] and [Signal], and see exactly where C# still has caveats.

Keep your C# moving from Unity to Godot
On this page

Switching to Godot does not mean giving up C#. Godot ships an official .NET edition that runs the same node tree, scene system, and signal model as GDScript, just with C# syntax, static typing, and the .NET toolchain on top. This guide sets up the .NET editor, walks through the PascalCase shift from Unity's C# API, ports signals and exported fields into their C# attribute form, and is honest about the one place C# still cannot follow you: the web export.

You Can Keep C# in Godot

Godot's C# support is not a community shim bolted onto a GDScript-only engine. It is an official build maintained alongside the standard editor, generated from the same engine API, so a node's methods, signals, and properties exist in C# with the same behavior as their GDScript counterparts. If your team already thinks in C#, has existing .NET tooling, or wants static typing and full IDE refactoring by default, that preference carries over directly. GDScript is not a prerequisite for using Godot productively; it is one of two first-class scripting options, and this article assumes you are choosing to stay on C# rather than being forced off it.

That said, C# in Godot is not "Unity C#" with a different import statement. The node and scene model, the signal system, and the physics and rendering APIs are all Godot's, not Unity's, so the concepts from the GDScript for Unity developers guide still apply even though you are not writing GDScript. Only the syntax you write to reach those concepts stays close to home.

Set Up .NET for Godot

The standard Godot editor download does not include C# support. You need the separate .NET-labeled build from the Godot downloads page, and you need the matching .NET SDK installed on your machine, because Godot does not bundle the compiler itself. Confirm the SDK version against the editor you downloaded before you start a project: Godot's own documentation is explicit that the required .NET SDK version has moved between releases, so check the current requirement for your Godot version rather than assuming last year's number still applies.

  • Download the .NET version of the Godot 4.7 editor, not the standard build.
  • Install a matching .NET SDK; Godot needs it on the machine to build your project's C# assembly.
  • Create or open a project, then build once from the editor so the C# solution and project files generate correctly.
  • Point your IDE (Visual Studio, Rider, or VS Code with the C# extension) at the generated .sln file for real intellisense; the built-in Godot script editor does not provide C# completion.

Do this before writing gameplay code. A project that starts on the wrong editor build, or with a mismatched SDK, tends to produce confusing build errors that look like scripting mistakes but are actually toolchain mismatches.

The API Casing Shift from Unity to Godot C#

The biggest daily adjustment is not a new language, it is a new naming convention layered on the same language. Godot's C# bindings use PascalCase for engine members, matching .NET convention, where GDScript and the underlying engine use snake_case. Lifecycle methods gain the same treatment: _ready() becomes _Ready(), _process(delta) becomes _Process(double delta), and queue_free() becomes QueueFree(). Where Unity's transform.position is a field you reach through a component reference, Godot's C# node exposes Position directly as a property on the node itself, because in Godot the node you write is the transform owner, not a separate attached component.

csharp
using Godot;

public partial class Mover : Node2D
{
    [Export] public float Speed = 200f;

    public override void _Process(double delta)
    {
        Position += new Vector2(Speed * (float)delta, 0);
    }

    public override void _Ready()
    {
        GD.Print("Mover ready at ", Position);
    }
}

Note the double delta in _Process and _PhysicsProcess: Godot's C# bindings use double for the frame delta, not float, which trips up code copied straight from a Unity Update() signature. Getters and setters that GDScript exposes as functions are frequently converted to real C# properties instead, so a call like set_name("Friend") in GDScript becomes a property assignment, Name = "Friend", in C#. When an unfamiliar member does not behave the way you expect, check the equivalents reference before assuming the C# binding is missing something; most of the time it exists, just under its PascalCase name.

Signals and [Export] from C#

Godot's signal system is not a GDScript-only feature; C# reaches it through two attributes. [Export] exposes a field or property to the Inspector, replacing Unity's [SerializeField] in spirit, though it operates on Godot's own property and Resource system rather than Unity's serializer. [Signal] declares a custom signal on a public delegate whose name must end in EventHandler; Godot registers the signal under the delegate's name with that suffix removed, so DiedEventHandler becomes a signal named Died.

csharp
using Godot;

public partial class Health : Node
{
    [Signal] public delegate void DiedEventHandler();

    [Export] public int MaxHp = 100;
    private int _hp;

    public override void _Ready() => _hp = MaxHp;

    public void TakeDamage(int amount)
    {
        _hp -= amount;
        if (_hp <= 0) EmitSignal(SignalName.Died);
    }
}

Connect a C# signal with the familiar += event syntax, and emit it with EmitSignal(SignalName.Died) rather than calling the delegate directly; the docs are explicit that you cannot use Invoke to raise a Godot signal, because EmitSignal is what actually notifies the engine and any GDScript listeners on the other side of the connection. That two-way compatibility is the point: a C# node can emit a signal that a GDScript node listens for, and vice versa, so a mixed-language project is not just possible, it is a supported working pattern rather than a workaround.

Drop the Update() Habit

Unity's MonoBehaviour.Update() and FixedUpdate() map onto Godot's _Process(double delta) and _PhysicsProcess(double delta), but the mapping is not just a rename. Godot expects every C# script that overrides these methods to belong to a node in the active scene tree, called automatically once that node is ready, rather than a MonoBehaviour attached loosely to any GameObject. There is no separate "component enabled" toggle to check first; visibility and the node's own ProcessMode control whether the callback runs. If a Unity script depended on Start() running once before the first Update(), the direct match is _Ready() firing once before _Process begins being called every frame, and _PhysicsProcess firing at the physics tick rate for movement and physics-affecting code the same way FixedUpdate() did in Unity.

Compare the Same System in GDScript and C#

Signature lab · Language decision

GDScript or C#? Compare the same node

Keep the gameplay behavior synchronized while the project constraints change.

GDScript hot-reloads; C# needs a build step, though it is fast.

GDScript
extends Node
signal died
func take_damage(amount: int) -> void:
    health -= amount
    if health <= 0:
        died.emit()
C#
using Godot;
public partial class Enemy : Node {
    [Signal] public delegate void DiedEventHandler();
    public void TakeDamage(int amount) {
        _health -= amount;
        if (_health <= 0) EmitSignal(SignalName.Died);
    }
}
Which statements are true for this project?
GDScript fit0
C# fit0

Select the constraints that can affect shipping.

Use the lab below to see the identical enemy-damage system written both ways: the same signal, the same exported field, the same lifecycle shape, only the casing and attribute syntax differ. Reading them side by side is the fastest way to internalize that this is one API with two front ends, not two different systems you have to learn separately.

Where C# Still Has Caveats (Web and Console)

Be honest with yourself about the one export gap that matters most: as of Godot 4, projects written in C# cannot be exported to the web platform at all, full stop, regardless of edition or build configuration. If shipping a browser build is a hard requirement, either keep that specific target on GDScript or accept that web is out of reach for the C# parts of the project; there is no partial workaround documented for Godot 4's web export today. Desktop export (Windows, Linux, macOS) has been solid since C# support first shipped, and Android and iOS export have been available since Godot 4.2, though the engine documentation still marks mobile C# export as experimental rather than fully mature. Console export in Godot is handled outside the public documentation tree entirely, through licensed console porting partners rather than the open-source export templates, so verify current C# support directly with whichever console partner your project uses instead of assuming parity with desktop.

When GDScript Is Still the Better Choice

None of this is an argument that C# is strictly better inside Godot. GDScript still wins for the fastest possible edit-and-run loop, since it hot-reloads without a build step, and it is still the only scripting option that reaches every export target Godot supports, including the web. A solo developer or small team with no existing C# investment often reaches working gameplay faster in GDScript precisely because there is one less toolchain between an idea and seeing it run. If your project targets the web, if nobody on the team has .NET experience worth preserving, or if the extra build step measurably slows iteration on a fast-moving prototype, GDScript is the more honest default, not a downgrade. The GDScript vs C# guide walks through that decision in more depth if you are still weighing the two languages rather than arriving with C# already decided.

Practice and Sources

Set up the .NET editor, port one small script from the casing examples above, and confirm it builds and runs before touching a whole Unity project's worth of code. The official sources below verify the setup requirements, the PascalCase and attribute conventions, and the current web export limitation cited in this guide; keeping C# is a legitimate, fully supported choice, and the migration hub has the rest of the porting path once your scripting language decision is settled.

Frequently asked questions

Do I have to learn GDScript to use Godot?
No. Godot ships an official C# (.NET) edition, and every core engine API is available from C# with the same node, scene, and signal model GDScript uses.
Can C# in Godot export to the web?
Not currently. As of Godot 4, projects written in C# cannot be exported to the web platform; if you need C# and web export together, that combination is not supported in Godot 4.
Is C# slower than GDScript in Godot?
Not for most gameplay code. C# has an edge in CPU-heavy simulation loops, but ordinary gameplay glue is dominated by engine calls, so the practical difference is usually small.
Verification notes

Sources and revision context

New for the migration spine; written for Unity developers who want to keep C# instead of switching to GDScript.