
By: Raul Santos
25 February 2023
The Godot 4.0 release contains multiple changes to almost every area of the engine, and first-party support for C# and .NET is no exception. The main focus of this release has been on modernizing the runtime environment and updating the C# API to be more in line with the engine core. We took care not to sacrifice that aspect of familiarity that many of you seek when picking C# as your main scripting language.
In this article, we’ll go into more detail about each of the most important changes.
- Runtime changes
- Engine interop with source generators
- The new Variant type
- Collections changes
- Signals as events
- Callable support
- Int and Float changes
- Renames
- Exported property improvements
- Improved C# documentation
- NuGet packages
- IDE support
- Future
⚠️ Warning
If you have created a Godot project in one of the earlier preview releases of Godot 4.0, you will need to delete the res://.godot/mono
directory before opening it in the stable version of the engine. This is required to ensure that no older assemblies are used.
Godot 3 projects can be converted to Godot 4.0 with the built-in project converter. Due to limitations of how the project converter works, the converted project will likely require lots of manual fixing before it can be opened with Godot 4.0. Because of numerous changes between the two versions you may want to create a new project from scratch instead of porting it.
Runtime changes
The main change for C# support is that we moved away from the Mono SDK and we now use the .NET SDK to embed the .NET runtime. This means we use the CoreCLR runtime for desktop platforms. Mobile and web platforms are not supported at the moment, and will be worked on for a future Godot 4 release. For the mobile platform specifically we are going to use the Mono runtime.
With this change, the Mono module is now called .NET module, but the only .NET language officially supported is still C#. The Godot API is also now targeting .NET 6, the latest .NET LTS release.
For end users this means that they only need to install the latest .NET SDK to start working with C# in Godot 4. The default target for game projects is .NET 6, allowing you to use modern C# features included in .NET; something that is not easily accessible in Godot 3. It should also be possible to target newer frameworks, although we can’t fully guarantee it would work properly.
In Godot 3, which targets .NET Framework 4.7.2 by default, you could use the latest version of C# just as well, because the language version is not inherently tied to the targeted framework. However, this is not true for all features, as some of them require runtime support (such as Default Interface Methods). Now that we officially support targeting .NET 6, all features available in C# 10 are supported.
Targeting the latest frameworks also allows using the newer APIs included in the Base Class Library (e.g. DateOnly
, TimeOnly
, PriorityQueue
) and benefiting from recent performance improvements.
Relevant links
- Merge .NET 6 branch with master (GH-64089).
- What’s new in .NET 5.
- What’s new in .NET 6.
- Announcing .NET 6 — The Fastest .NET Yet.
Engine interop with source generators
In the past, Godot has used reflection to communicate with the engine. This has worked great so far but it comes with some limitations. We now use source generators, which have the following benefits:
- Generated code is generally faster than reflection.
- They support trimming which reduces the size of the exported assembly and paves the way for full AOT support in the future.
- They allow analyzing user code to emit diagnostics at compile time. This means that we can warn you about code that would fail at runtime before you actually execute the game.
This change also means that user types which derive from native Godot types (such as Node
or Resource
) need to be declared partial
.
Unfortunately, currently this makes it difficult or more cumbersome for third-party source generators to support some workflows. We are looking into ways to support these use cases, so keep an eye out for that in the future.
Relevant links
- Merge .NET 6 branch with master (GH-64089).
- C#: Generators ignore _Ready method in partial classes from another generators (GH-66597).
- Allow for a way to ensure some source generators run before / after others (GH-57239).
The new Variant type
Previous versions of C# in Godot have used System.Object
to represent Godot’s Variant type, which is a special container for various data types supported by the engine. The problem with that is that System.Object
is the base type for all .NET types, including types that are not supported by Godot. This means that the Godot API allowed using types that would fail at runtime since we wouldn’t be able to convert it to a type that Godot would understand.
Now we have a dedicated Variant
struct that represents all the types that Godot supports. This means our API is more strict about which types you can use, and you will get errors at compile time before executing your game.
The way the Variant
struct is implemented also avoids boxing value types (such as int
and float
) in cases where it was unavoidable in Godot 3.
When using System.Object
, users have been able to take advantage of .NET features such as pattern matching and generics. The new Variant
struct no longer allows to use those features directly. Pattern matching can be replaced with checking the Variant.VariantType
property, while generics can be supported using the [MustBeVariant]
attribute.
For more information about how Variants are supported in C#, take a look at the new documentation page about C# Variant.
Relevant links
- C#: Represent Variant as its own type instead of System.Object (GH-3837).
- Merge .NET 6 branch with master (GH-64089).
- C#: Optimize Variant conversion callbacks (GH-68310).
Collections changes
Just like in 3.x, Array
and Dictionary
collections are supported in C# through the types defined in the Godot.Collections
namespace. Godot’s packed arrays, a special type of arrays optimized for memory usage, don’t have their own representation in C#, and normal C# arrays should be used (e.g. byte[]
).
Godot APIs are only able to understand these Godot collections, which means anything that interops with Godot’s native side needs to use one of the supported collection types.
In 3.x, the non-generic collections used System.Object
, just like .NET non-generic collections. However, thanks to the new Variant
type, Godot C# collections now implement the generic collection interfaces where T
is Variant
. This means that the non-generic collections now only support Variant-compatible types, so you’ll get compile errors when using an invalid type rather than a runtime exception.
The generic collections will also validate that the generic T
parameter is one of the Variant-compatible types, since it’s annotated with the new [MustBeVariant]
attribute.
Godot collections now implement more utility methods that expose similar functionality to what can be done in other supported languages. Using these instance methods is generally faster than Linq because it avoids marshaling every item in the collection. On top of that, collections also provide an extra utility method, MakeReadOnly
. It allows you to freeze the collections so modifying them is no longer possible.
You are not limited to using Godot collections in your own internal logic, of course. The Base Class Library in .NET includes many other collections that can be used. These collections are not going to work with Godot’s API but can still be useful. In 3.x, some .NET collections were supported in very specific scenarios (such as exporting properties). This is no longer supported in 4.0 since it required reflection. Support for .NET collections may be re-introduced in the future, implemented with source generators instead of reflection.
To help you choose which collection type to use and when, we have a new documentation page all about C# Collections.
Relevant links
- Merge .NET 6 branch with master (GH-64089).
- C# Export on List not possible anymore (GH-70298).
- Sync C# Array with Core (GH-71786).
- Sync C# Dictionary with Core (GH-71984).
Signals as events
Godot signals are a great way to implement the Observer pattern. It’s possible to use Godot signals by using the common engine API, such as the Connect
and EmitSignal
methods. But C# developers are more used to using events, as they are more idiomatic.
For Godot 4.0, we now generate C# events for all Godot signals, which allows developers to use the event syntax that they are used to and love