Cross-language scripting in Godot is goddamn magical

Jonas Hundertmark
medialesson
Published in
3 min readJun 27, 2023
Photo by Michael Dziedzic on Unsplash

If you are writing behaviour scripts in Godot you usually get two options: C# and Godot’s own GDScript. Personally, I love working in GDScript. Its concise syntax and high-level approach make coding fast and easy. But there are situations where C# is the better choice.

C# remains a good bit more readable in larger classes. Its stricter typing means you are more likely to spot mistakes during design time. It’s a tiny bit faster as well. But maybe the biggest reason to use C# is the .NET ecosystem. There are thousands of useful NuGet packages you can leverage for your apps, and since Godot 4 is running against .NET 6 and up, most of them work perfectly out of the gate.

The great thing about Godot is, you don’t need to choose one language to write your entire project in. You can mix-and-match GDScript and C# as you see fit, getting the best of both worlds in the process. This raises the question though: How well do these two very different languages actually work together? The answer is very, and oh god, it’s such a delight.

Let’s start with calling GDScript methods in C# because it’s the clunkier of the two ways. Calling functions and getting variable values in GDscript works via the .Call() and .Get() methods, respectively. Because C# is strictly typed and doesn’t support duck typing, we need to call these members via magic strings. Calls to functions that don’t exist will fail silently.

For built-in variables and calls, Godot has special properties called MethodName, PropertyName and so on. Unfortunately we can’t use them if we want to access GDScript members.

However, usually we want to call the much more powerful C# classes from within GDScript. And that way, cross-language scripting becomes almost seamless.

To start with, any public method or variable can be directly accessed through GDScript’s duck typing, so there is no need for .Call() and .Get() functions. But, it gets better:

Observant readers might have already noticed that Godot’s powerful Variant type means there is an amount of ambiguity when calling C# functions from GDScript, as the compiler sometimes can’t infer the variable type at compile time. Rather, Godot will try to automatically cast to the desired C# type at runtime, which means we can do unhinged stuff like this:

Obviously, we won’t usually add random variable types together like this but the fact it just works without issue shows how robust and easy it is to work with multiple languages inside the same Godot project.

Limitations

Like I said earlier, MethodName and PropertyName won’t work for GDScript names, even if that script has a class_name, since C# cannot access GDScript classes as C# classes. You’ll have to work with magic strings.

Async/Await also doesn’t work. GDScript doesn’t know how to await C# Tasks and vice versa. If you want to implement asynchronous behaviour between classes, the preferred way to go about it is to use Signals.
Always Remember: Call Down, Signal Up

Other than those two, basically everything I could want is here and it works without hassle. A few days ago, I needed a certain NuGet package for a service I was building inside one of my Godot apps and I was blown away by how this engine alows you to just quickly hop over to C#, do what you need to do and go back to GDScript without virtually any boilerplate whatsoever. It’s incredibly comfortable and super reliable. I’m a big fan.

Cheers!

--

--