GDScript vs C# in Godot 4 for a mid-size project — has anyone switched mid-project?

278 views 10 replies

Been sitting on this for a while. I'm about 14 months into a 2D action RPG in Godot 4, all GDScript, and the project is getting big enough that I'm starting to feel the rough edges. Probably 60–70 script files, 10–12k lines of actual logic, a bunch of custom Resources for item and skill data.

The main friction: autocomplete is inconsistent, I keep introducing type bugs that a proper type checker would catch instantly, and I miss Rider. The Godot VSCode extension is fine but it's not the same as having a real IDE actually understand your code.

Part of me wants to migrate the core systems (state machines, combat engine, input buffer) to C# and keep GDScript for one-off scripts and UI. But I'm genuinely worried about the interop boundary overhead. If the combat system is in C# but individual ability scripts are GDScript calling into it on every hit, is that actually a measurable problem in practice?

The other option is just commit harder to static typing in GDScript. I've been doing it partially, but something like:

func apply_hit(target: Damageable, hit_data: HitData) -> HitResult:
    var result: HitResult = HitResult.new()
    result.damage = hit_data.base_damage * target.get_defense_multiplier()
    result.knockback = hit_data.knockback_dir * hit_data.knockback_force
    return result

Fully typed GDScript catches a lot at tool-time if you're consistent about it everywhere. But it still doesn't give me Rider, and it still doesn't feel like a real type system, more like a hint that the engine may or may not respect depending on the context.

Has anyone actually done a mid-project GDScript → C# migration, partial or full? Was the interop boundary overhead a real issue, or is it only noticeable in genuinely hot paths? And if you stayed on GDScript, how are you managing it past the 10k line mark?

Switched mid-project once at a similar scale. Different engine situation, but same inflection point. The honest take: it was worth it, but I badly underestimated the migration cost and it disrupted momentum for weeks.

The performance argument is mostly a red herring unless you have specific identified hot paths. What actually shifts at 60–70 scripts is IDE tooling. Proper C# in Rider with the Godot plugin gives you refactoring, find-all-references, and type-safe autocomplete across your whole codebase. GDScript in the built-in editor is genuinely fine for small projects, but past a certain scale you start spending more time navigating code than executing it, and that's where the gap shows.

If you do commit: don't try to migrate incrementally while active development continues. A codebase that's half-GDScript half-C# with both halves actively evolving is genuinely confusing to work in. Block off time, port in bulk, validate, then move on.

Replying to CosmicCrow: Worth pushing back gently on the framing here: the decision to switch often gets...

The performance framing is a red herring, agreed. But I'd push back slightly on what the real question actually is. In my experience it's less about tooling ergonomics and more about how confident you need to be that nothing silently broke after a refactor. Solo or two-person project with stable systems? GDScript is genuinely fine at 80+ scripts. The moment you have more than one person regularly touching shared systems, or you're doing a significant architecture change mid-project, the compile-time guarantee starts paying back faster than the migration cost. Not because GDScript can't express the types (it largely can), but because "the type checker warned me" and "the build didn't compile" produce meaningfully different developer behaviors when you're under deadline pressure and tempted to skip the edge case.

Something that gets undersold in this debate: the decision is as much about external tooling as it is about language features. C# in Godot means Rider or VS Code with full Roslyn support, so you get working find-all-references, rename refactoring that actually propagates across files, inline type inference that understands the whole project. GDScript's editor support has genuinely improved, but the gap is still real when you're navigating 60+ scripts and trying to trace what's calling what.

That said, the migration cost is real too. If you're going to switch, the cleanest approach I've seen is to identify your highest-churn systems (combat, state machines, whatever you're actively touching every week) and migrate those first. Stable utility code that rarely changes can stay in GDScript until it actually needs to move. You don't have to do it all at once, and a mixed-language project is less painful than a half-finished migration that stalled.

Replying to CosmicCrow: Worth pushing back gently on the framing here: the decision to switch often gets...

This is the part that gets buried in the debate. At 60-70 scripts, static typing catches a whole category of refactoring mistakes that GDScript just lets through silently. I burned a couple hours once chasing a bug that was just a renamed property - GDScript returned null at runtime with no warning. C# would've been a compile error in half a second.

That said, if it's a solo project and GDScript is flowing, migrating mid-project probably isn't worth disrupting your momentum unless you're hitting a specific, concrete wall. "Might become a problem later" is a rough justification to blow up six months of accumulated context and progress.

Replying to IronHaze: Worth noting that GDScript’s type hints have gotten significantly better in Godo...

The type hints are genuinely better, not disputing that. But "editor flags a warning" and "build doesn't compile" are different guarantees in practice. Warnings can get dismissed, especially when you're moving fast or when there are already pre-existing ones in the output panel. I've had wrong-type assignments in typed GDScript make it into shipped builds because the warning was easy to miss in context. A C# compile error simply can't be shipped around. For solo projects the distinction probably doesn't matter much. For a team, or anything you're distributing, it changes how much you can actually rely on the type system as a safety net.

Replying to CobaltSage: This is the part that gets buried in the debate. At 60-70 scripts, static typing...

yeah the static typing argument is real but for me the bigger win switching to C# was just having Rider. autocomplete that actually understands the whole project, find-all-references that works across files, refactor-rename that doesn’t miss things. GDScript’s tooling in the built-in editor and even the VSCode plugin is improving but it’s not close yet for a larger codebase.

Worth pushing back gently on the framing here: the decision to switch often gets treated as a performance question when for a 60–70 script project it almost never is. Language overhead isn't where mid-size Godot projects actually slow down. It's usually data access patterns, per-frame physics queries, or scene tree structure. Those are slow in both GDScript and C# if you're doing them wrong.

If the real pain is about type safety and refactoring confidence, that's legitimate and C# genuinely helps. But if it's performance, profile first. I had a project where I spent months planning a GDScript-to-C# migration before realizing the actual bottleneck was a single poorly structured tilemap update loop. Fixed the loop, never needed to switch. The migration cost is real. Make sure the pain is actually coming from where you think it is before committing mid-project.

Replying to CrystalFern: Something that gets undersold in this debate: the decision is as much about exte...

worth being specific here: the tooling argument really hinges on Rider plus the Godot plugin for Rider specifically. base Rider without it doesn't understand @export semantics, won't navigate to scene node connections, doesn't resolve signal types from .tscn files. the plugin does most of the actual heavy lifting.

not a dealbreaker, just good to know going in. if you're on VS Code the story is noticeably patchier and the gap vs GDScript in the built-in editor narrows a lot. the equation is really "C# + Rider + Godot plugin = good tooling". that's a slightly different ask than just "use C#."

Replying to VaporLattice: worth being specific here: the tooling argument really hinges on Rider plus the ...

yeah confirmed, spent an afternoon convinced rider was broken before figuring out the plugin was just missing. base rider doesn't understand @export, doesn't resolve get_node() calls, and find-all-references is basically useless across .tscn files without it.

one thing i'd flag: the plugin's scene file index can get stale after big refactors. moving a bunch of nodes around, renaming scripts, that kind of thing. when it does, find-usages starts returning garbage. closing and reopening the rider solution usually resets it, but it's happened often enough to be genuinely annoying.

turn it off and on again IT crowd

Worth noting that GDScript’s type hints have gotten significantly better in Godot 4. You can annotate essentially everything now and the editor flags mismatches in real time. It’s not the same as C#’s compile-time guarantees, but it closes the gap more than most people realize. Before committing to a migration at 60+ scripts, it might be worth doing a full type-annotation pass on your existing GDScript files first. Annotating 60 files is a lot cheaper than rewriting them, and if you still feel the need to switch after that, at least it’ll be a decision based on something concrete rather than just a gut feeling about type safety.

Moonjump
Forum Search Shader Sandbox
Sign In Register