Is Godot Gradually Adopting C# as Its Primary Programming Language?

What is that a reply to?

I believe 4543s54 is referencing this:

1 Like

Choosing C# as the primary language for Godot would be a mistake. C# is a great language—one of my favorites, actually. It’s easy to learn, has a ton of built-in libraries, and you can get started quickly. But Godot is written in C++, and for game development, C++ is the better choice, hands down, because of performance.

It is not a mistake, it’s just simply being one of the many but loved by both the professional and the whole game industry. C# has a high affinity to C++ and Java syntax, we all know, but it also plus a lot of extra goodies such as Lambda Expression/Expression Tree, LINQ, “unsafe”, and an almost-seamless integration and packaging system without much extra tuning makes it a perfect choice for rapid game design. As a software developer before, I have personally struggled with Java JAR packages and C++ headers altogether. I never had this issue with C# and dotnet ecosystem overall.

The main issue with using C# in a game engine like Godot is the performance hit you get from native interop. Managed languages like C# just don’t play well with native code. P/Invoke, for example, is notoriously slow. Sure, there are ways to reduce the impact, but in many cases, it’s still not good enough. Unity has tried to address this with IL2CPP, which converts managed code to C++, but even that isn’t perfect and isn’t available in Godot anyway.

That’s because P/Invoke (or DllImport) has to figure out how to correctly generate a translator trampoline delegate function to that native API and serialize and deserialize (or in dotnet’s term, marshalling/unmarshalling) the dotnet object for the inputs to the native API and feed the right output for the dotnet environment. Since dotnet allows self-modifying code, you have to make the worst assumption that all the inputs are going to be dynamic.

And you know, there are tens of thousands of parameters, say like you need to convert the C# string to UTF-8 string, that involves a re-encoding which means extra heap clones and the decoding it back to 16-bit Unicode which dotnet demands, and there will be a lot of edge cases, ABI differences and translation strategy also needs to take in account, so while you can generate that trampoline very quickly, but it could break the entire process if things went south, and so you either trade performance or compatibility/safety.

Dotnet simply chose compatibility/safety’s first and performance second. Not to re-mention that dotnet VM itself is mostly implemented with JIT, and that trampoline function is also usually cached for subsequent calls, so the toll to pay isn’t really that much, and most of the performance hit comes from that automatic translating which can be specialized by the developers for further performance tuning if you will, but we all simply agree to go lazy because for that, it is reasonably good already for the autogenerated trampoline, at the very least, that’s way better than JNI…

Now that with NativeAOT, things are going even further, by assuming all the code would be static and no dynamic codegen, then we can safely assume the P/Invoke call to have a static call site as well.

When I play games, I can often tell if they were made in Unity because of the frame spikes and performance issues. Unity uses C# as its main scripting language, and that’s a big reason why. It’s too easy to introduce performance problems with C#, especially with things like garbage collection and native interop. GDScript or Lua, on the other hand, sidestep a lot of these issues and make it more obvious when you’re doing something wrong.

That’s because Unity itself is not focused on performance in the first place. It is focused on capabilities, composability and compatibility. That’s why while it uses C# and it does have a sub-par performance, but it won the market.

I would also mention that GDScript and Lua are also both garbage collected languages that does not have JIT (with the obvious exception of LuaJIT), but they also have a way more primitive GC design, which is accidentally well-enough to fit in for games, since games can be seen as a soft-real time dynamical system, while for dotnet, it has to take in account for more things, so it focuses on general use case rather than gameplay.

C# makes much more Generational GC collections and memory compactions automatically at an unpredictable time, so as to keep heap memory usage in-check and well aligned for high-performance access, compared to a simple Mark-and-Sweep when memory is not enough, and that means GC kicks in way less for GDScript and Lua. That’s simply the difference you noticed in the frame spikes.

For Godot, the best approach is to lean into GDScript or another lightweight scripting language, and handle the heavy lifting in C++ through modules or GDNative extensions. C# can work in Godot, but it shouldn’t be the go-to option. If Godot were built from the ground up as a .NET engine, that would be different, but it’s not. Performance is key for most games, and C++ is the way to get there.

Again, while performance does play a big factor for most game, it is not the key for most games. Making the market’s demand, is and being the only key for most game. There are a lot of games that is terribly unoptimized, but it sells well.

Take Quake Champion for example, you have a game that runs well, but it won’t sell well; Take Palworld, for example, you have a game that runs like crap, but it sells well. Plus, if you think of it the other way, you can reinvest the money you make to optimize incrementally, which is what Cyberpunk 2077 eventually regained.

So, pick either one of the pills, Neo.

C++ isn’t as tough as people make it out to be, especially with modern tools like std::unique_ptr and std::shared_ptr making memory management easier. If you’re serious about game development, learning C++ is worth the effort—it’s not as scary as it seems, and it’s crucial for getting the most out of Godot.

Smart pointers also introduce problems, for example you want to use “unique pointer” then you need to know std::move and std::forward, and thus move semantics works and how to take in account for cv-qualifications and lvalues/rvalues/xvalues, and evade the common pitfalls, for “shared pointers” which is reference counted so you have to assume the target platform have atomic instructions which may not be a problem for Godot’s target architectures but generally speaking, not always ideal in theory because those MCUs often do not have those atomic stuff.

Also, both of these smart pointers are wrapped with a template class in the standard library, which can have different implementation across different compiler toolchain, meaning you cannot easily make the assumption to pass the smart pointer data across different program boundary easily, since you can have a different data structure generated for different template parameters. Can you shove a MSVC shared_ptr to an exported GCC function? That’s the problem.

1 Like

I can’t see a full shift of the engine to C#, but continued expansion of support and feature set is something I hope for and is already in progress.

I personally think the power of C# out ways it’s missing support, but I understand most people not seeing it that way.

Overall, an increase in support is coming and is already being worked on, and personally I find C# to be more powerful than GDS.

3 Likes

AFAIK GDScript is not a garbage collected language, RefCounted objects are freed instantly when they’re not used any more. The only mention of a GC or garbage in the Godot docs is always when they’re talking about C#.

Incorrect. C# is 100% NOT proprietary. It was opened sourced under the JIT years ago. C# is orders of magnitude faster than GDScript when you have thousands of objects in a scene at the same time. GDScript might be “fun” to program in but it is not the answer for anyone who understands and appreciates the point of a type system.

2 Likes

This reminds me of all the developer discussion about how javascript isn’t a real programming language and shouldn’t be used for server side programming because it’s just a web scripting language. And then how it shouldn’t be used because it isn’t typesafe. Now a lot of web developers use React and Typescript.

I actually agree with @RichmarIII about C++. That’s what developers at commercial game studios are using. Both C++ and C# come with payoffs in time needed for a developer to understand more to take advantage of them.

GDScript is a much easier entry point, and has allowed a lot of easy adoption. C++ and Assembly used to be the best language combo for making drivers and doing other low-level hardware programming. People got tired of that and made Rust, and now it’s easier to use and more performant.

It is true that C# is more performant than GDScript. I am curious about the latest benchmarks. However, despite that I haven’t hit that wall yet, and I like GDScript a LOT more. As someone who has been a professional developer over the last three decades in C++, C#, Java, Python, Ruby, and JavaScript, I happen to think GDScript is awesome.

The amount of progress they have made just from 3.x to 4.x is significant. The performance changes they have made just from 4.3 to 4.4 are impressive. What I love about GDScript is that the team continues to work on the heavy lifting of performance, IDE features, improved shader functionality, etc. I can just focus on making my games. And if I run into a problem, I can create or add info to a bug report - and it gets handled!

Everyone has different things they want out of programming languages. That’s why Godot supports C#. Yes C# comes with a lot of things. They are both good and bad. I decided a while back to create a whole game in Godot C# for a game jam. I enjoyed the learning experience. I do think everyone should try using C# in a game. I learned a few tricks from the course I used that I was able to take back to GDScript with me.

I loved C# when it came out as a language. It resolved my biggest beef with C++ which is I didn’t want to deal with memory management. I also happen to love Java for the same reason. I love GDScript because it handles lots of other things for me. Now that I can statically type everything, I do. Since I can’t do templating like I can in C#, I use Variants instead.

All the replies in this discussion come from personal preference and experience. Everyone’s experience is different. My personal preference is GDScript. I’m very happy that there are people pushing the C# side forward, because as more Unity and/or Unreal people come over for that, the community grows and we are all enriched.

I also am a big fan of GDScript continuing as the primary language, because I have a feeling that it will continue to become more performant. The more C++ developers the project attracts, the better for performance. Because every language I have mentioned is written in C++: C#, Java, Python, Javascript, Rust, Ruby, GDScript, even C++ itself. So just because C# has better performance now, doesn’t mean it will continue to be so in the niche of Godot games.

Having said all that if you primarily develop Godot in GDScript, I recommend trying out C#, and if you primarily develop Godot in C#, I recommend trying out GDScript. I used to have a number of preconceived notions personally about C# in Godot, having been a professional C# developer, but not having used it with Godot. I was wrong on a number of points. I highly recommend Godot 4: Build a Feature-Packed 3D RPG for trying out GDScript, and Godot 4 C# Action Adventure: Build your own 2.5D RPG for trying out C#. Mina Pecheux’s videos and books are good for C# development too.

2 Likes

@dragonforge-dev, you’ve refuted your example within your example - TypeScript is a superset of ECMAScript. Consequently, its existence provides credence to the criticisms that you reference.

Likewise, the undermentioned is inequivalent:

GDScript might be memory safe, but it’s not more performant. Actually, neither is Rust inherently. Instead, due to the complexity of memory management at the low level that Rust operates at, its incredibly well-designed memory management tends to be better than what a human can design. To my knowledge, it shan’t beat someone who writes the appropriate compiler directives.

For you, perhaps. Not I. The mere fact that C# can be utilized in more contexts than the Godot Game Engine is why I utilise it. I wouldn’t have bothered to learn GDScript nor choose Godot, had GDScript been the sole option, for I wouldn’t have desired the vendor lock-in.

Having GD be an importable library is brilliant, since it means that I can utilize any IDE that supports C# (like the venerable VSC). Any external support for GDScript shall be second-rate to Godot’s.

1 Like

What ? Can you expand ?

Why would learning GDscript be bothering ? IMO for experienced developers is not bothering at all. It took me few hours to get familiar with the syntax.

What takes time to learn is: the editor, the composition design pattern, the type of nodes and what they do, and so on… Not which language you use.

I agree that GDscript type system is lacking a bit compared to other languages, but it is evolving as we speak - look at version 4.4

I don’t see why it will not continue to get better in this regard.

Why blame the language ? This is not objective truth, it might be low code quality or bad architecture.

2 Likes

Apologies, I’m not quite understanding what you’re saying I refuted. I was just saying that over time, things got better. I don’t refute what you’re saying, you may know more than me on this subject.

Again, apologies if my communication was poor. I did not mean to say that GDScript was more performant. Quite the opposite.

As for the Rust example, that came from developer friends who use it professionally. I do not claim any expert knowledge in Rust. You may be correct about writing compiler directives, but then again, we go back to Rust being very well-designed for a particular set of use cases to which it is quite good. This gets into a larger discussion of developer time over code performance.

My point was just that people using Rust are not then going in and hand-coding in C++ and Assembler because the point of the language is that it is good enough. Again, to be clear, the information that it is faster than hand coding is anecdotal, but comes from developers who actively use it in their jobs.

Those are fair points. Like I said, I’m glad there are people like you that are championing C#. Your deep knowledge of the language will help that side of Godot grow and become richer - and easier to use over time.

When I started really digging into Java about 10 years ago, I learned a lot from developers who had only used that language for their entire careers. They really helped me out a lot. On the flip side, they told me that Reflection was impossible in Java (once I had explained what it was). I had to figure that part out on my own because it wasn’t really used much. Then I got to show it to them and they were able to help me optimize it even further. What I love about different perspectives is how we can enrich each other’s knowledge and learn from others’ experience.

I totally see your point about vendor lock-in. I’m glad you have the option of C#. That was my only real option (IMO) when I was using Unity and Unreal. I happen to feel the opposite. Early in my career I was locked into C++ jobs. I didn’t like that and have chosen to learn new languages when it makes sense. In my example above about Java, I picked the language because I polled all the other people using the tool I was creating, and everyone knew Java. I had previously made versions of the same tool in C#, Java, Ruby and Python at other companies. At that point, my experience with Ruby was the most recent, and I was really interested in applying what I learned about Reflection in Ruby to Java. I believe that is why Godot supports C# - because it is the most commonly used game development language.

I tried GDScript because I happen to really like Python and Ruby, and GDScript was based on Python (and much more python-esque when I started in 2.x). I liked that GDScript is faster for prototyping. Now I make sure that everything is typed. If I’m using a Variant, I call that out.

In both of the courses I linked in my previous post, I took issue with some of the coding practices of the instructors. I optimized a number of things in my own code while following the lessons. Despite that, I learned a lot from each of them because their specific knowledge of Godot and the language they were using was deeper than mine.

Also, a note about easy adoption. I didn’t mean for me. I meant for people who are coming to this hobby without any programming knowledge. GDScript is a much easier entry point for people who are learning to program. When I started using C# with Godot it was relatively easy for me. I already had VSCode installed - I just had to add a few plugins.

1 Like

@Gass, be careful to not misquote, for I deliberately utilised “bothered” in the stead of “bothering”. The latter wouldn’t have been syntactically sensical.

Regardless, I’m familiar with C#, and utilise functionality like its type system to improve my code. GDScript lacks more than merely this, though. I see no reason to learn a slower language with less functionality when I can learn quicker ones with more in its stead. Heck, if I learnt C++ (and had 50 GiB of free storage), I could use UE, which provides more than speed!

More importantly, though, I frequently utilise external libraries that integrate with C#. As a comparison, imagine asking someone who utilises Python 3 for data science to migrate to C#. It would be a monumental task, for the libraries they previously utilised would be unavailable.

This is inherent when moving from one language to another. An example is the Linux kernel now, for although Rust is serving it well, maintaining two bindings for C and Rust has been a time sink.

I doubt that anyone doubts that. However, deciding based upon an expectation that the language shall gain such a significant feature appears very optimistic.

Though, you’d not expect a game written in Python to be performant.

Irrespective, you’re probably correct, considering it’s designed for a game engine. @Armynator, were you running those games via a package, or from the game engine? I ask because the undermentioned may be of note:

  1. The subsequent comment in its thread explains how GDScript can be quicker in some scenarios, which is relevant to the overall conversation here, too.

  2. reddit.com/r/godot/comments/uutwra/comment/i9hvek4

2 Likes

@dragonforge-dev, thank you, although I think you might hold my response in too high of a regard. If your sources are those intimately familiar with Rust and C#, I’ll defer to their opinions - in comparison, I am probably a novice. My specialism is system administration, not software development.

I love Python too, although mostly just because it has Qt bindings.

I prefer to give people the benefit of the doubt. People are usually doing the best they can.

One of the things I like about modern languages is they handle a lot of low-level things better than I can. I used to have to choose either a switch statement or an if-else set of statements because it affected performance based on the number of choices. Modern languages don’t care, and I can write readable code. Underneath, the compiler or interpreter chooses the most performant way of storing my instructions.

I actually really enjoy writing Assembler. I also love bit shifting and bitmasks. But none of that is necessary for almost anything I do these days. About 15 years ago I was doing some hardcore modding of Neverwinter Nights, and they allowed adding limited ints to custom objects. So I was using bitmasks to store a bunch of complex information. Optimization is fun, but thankfully it isn’t as necessary these days.

Hey I learned something new! I just looked up Qt. It reminds me of Tk, which I used to use 20 years ago to make GUI interfaces on FreeBSD.

1 Like

Without joining the discussion too much, I’m more than happy to share my experience as a developer who originally started off as a C# developer making WinForms apps, then transitioned to web development (also with C#), and eventually picked up game development as my main indie job, which I still do to this day.

I’ve been using C# seriously since around 2017-ish. When I first started game development, I was more or less forced to use GameMaker, an engine that had very little freedom when it came to what programming languages you used. Sure, you could somewhat use C++, but it was VERY difficult to write a proper addon for the engine that worked right, and developing the whole game in C++ was really silly and probably not worth the trouble. (At that point, just use an engine that supports C++.) I spent around 2 years using GameMaker and its own scripting language called GameMaker Language (or GML for short). I personally never really considered performance an issue from a programming perspective, as with most modern engines, what you do in regards to texturing, 3D modeling, culling, and such is what REALLY matters. You’d have to write some truly horrible code in a lot of places to notice a big difference in performance, but even in those cases, if you take care of all the non-coding-related things, you’ll most likely be fine. (Undertale is a good example; I never noticed performance issues, even though the code is sub-optimal, as explained by the creator too.)

However, at one point, both my friend (who does all the art) and I agreed to move from GameMaker over to Godot, and I immediately decided to pick up the .NET version of it since I was super familiar with C# anyway.
So far, the only negative thing about using C# with Godot is that the plugin scene is very split. Most developers who use C# aren’t compelled to write plugins for the masses using it because you specifically HAVE to use the .NET version to use said addon. But aside from that, I love the fact that I can use really old code I made years ago for other projects in Godot now too! I can just import a module I made and it just works. This is something I wouldn’t be able to do with GDScript, as I would have to re-write so many things. Even if I know “how it should be written,” actually re-writing many smaller modules in an entirely new language that I never used before would be a HUGE time sink. For most indies (including me), having an MVP is much more important than “picking the ‘right’ language.” Having access to NuGet packages and my old modules is a massive reason why I’m happy Godot has C# support, and I’ll continue to use it.

All in all, I don’t think GDScript should be “dropped” or that C# should be the main language, not at all. But I do hope that one day there will only be one version of the engine that natively supports both GDScript AND C#, and all the plugins written in C# will also work for people who only want to use GDScript and nothing else.

2 Likes

Hear, hear! I have considered starting to use the .NET version because it supports both C# and GDScript.

2 Likes