Notes on Using C# in Godot


Game development Godot CSharp

Notes on Using C# in the Godot game engine.

Over the past couple of months, I have been working in the Godot game engine. It primarily uses a custom version of Python, called GDScript. While I'm not entirely against Python, I'm more comfortable with languages like PHP, so I figured I would give their C# build a shot. It's not perfect. I took notes as I went through different tutorials online, and also while creating my own RPG (possibly a future blog post coming for that). Here are critical things that I learned.

General Info

First of all, if you're using Linux (like me), you'll want to install Mono and avoid the VS Build Tools altogether. Mono worked fine for me once I was able to get it up and running. After that, you can install Godot by using some kind of package, building it from source, or downloading it from their website and extracting it to your preferred location (I opted for /opt/Godot/).

Godot lists the API differences of C# against GDScript on their documentation website. Important details such as how to correctly use exports, signals, and other syntax notes are included. Sometimes, you may need to build the project in order to see export variables and signals on your nodes.

Unintended Side Effects

Using async and await can have unintended side effects. Signals are used quite often in Godot, so you must make your functions async and you must await an object by specifying a Signal that is a part of that object. The issue comes with writing this functionality like you would in GDScript.

using Godot;

namespace GodotProject.Action
{
	public class Action
	{
		private Character _character;
		
		public override void _Ready()
		{
			base._Ready();
			
			_character = GetParent();
		}
	
		// Here, we wait for the character
		// to signal the action as completed.
		public async void Interact()
		{
			_character.DoAction(this);
			await ToSignal(_character, "ActionFinished");
			// ...
		}
	}
}

The issue here is that _character.DoAction(this) might emit the ActionFinished signal faster than the Interact() function can catch it, thus resulting in a “soft lock” in the game (the game would endlessly await for a signal that has already emitted). How do we solve this? By creating an awaiter variable before we call the function, and then awaiting that variable.

// Here, we wait for the character
// to signal the action as completed,
// but we make sure to get the awaiter
// object as a variable BEFORE calling
// the function.
public async void Interact()
{
	var awaiter = ToSignal(_character, "ActionFinished");
	_character.DoAction(this);
	await awaiter;
	// ...
}

This was easily one of my most frustrating issues that was quickly resolved once I figured out what was happening. ToSignal also only takes objects, so doing something like the following would result in an error if _character.DoAction() does not return an object.

public async void Interact()
{
	// ERROR!!
	await ToSignal(_character.DoAction(this), "ActionFinished");
}

Godot C# Oddities

Godot's core language is GDScript. C# is still a WIP (although very usable). If you read the documentation, you will understand that. There are some things that the documentation does not seem to mention, however.

Despite developing in C# (where it is usual to use doubles), you will be using float values. Any time you are sending data to or receiving data from Godot's objects, you will use floats. As I was new to C# (and coming from PHP…where anything is possible!), it took me awhile to get the hang of this. At first, I prefixed variables using (float), but eventually found out that you can just add an “f” to the end of a value (0.05f).

Godot also uses Arrays in most of it's functions. Again, new to C#, I didn't realize you had to always give an Array a size before it could exist. Most online searches resulted in “Why are you using Arrays? Use Lists…", which leads me to believe that Lists are more common in the C# world. I did, of course, result to using Lists and then using the ToArray() method.

Existing Bugs

Sometimes, you run into things that just don't work. You spend time on it for hours, looking up all sorts of variations of the error(s). Very rarely, you find an issue with the engine (or whatever it is you're using / working with). You hesitate to make an issue (or even to reply to ones already created) because you just can't believe there's something wrong and it has affected you. But, that's what happened to me - what's worse is that the bug is still in the engine and it's been there, known about, for three years.

The bug I ran into was an issue with Editable Children. In Godot, it is common to make a scene that you will re-use. If you add a scene to another, then you can further edit the child's nodes by switching the “Editable Children” flag to “on”. My issue comes with duplicating the child node when this flag is set to true.

Documentation Semantics

Godot's documentation is very good. It's some of the best documentation I've come across. However, no documentation is perfect. One particular issue I had with it is the Call method. Typically, you'll check if the method exists, and then call it. No issue there. The problem is with how the parameters are written in the example in the docs. It specifically notes that it supports a variable number of arguments, but the example (call("set", "position", Vector2(42.0, 0.0))) uses what could be thought of as a parameter name, "position". There were a couple of times where I came back to this documentation and thought that way, plugging in my parameter names for the arguments, only to result in a big, fat error. In reality, not a big deal - just something that caused a little bit of frustration for me and was part of my experience.

Final Thoughts

I really enjoyed learning Godot. I have always wanted to really learn a game development engine - being able to do that for two months was great, and I was able to produce an RPG that made my friends laugh. I will continue to work and learn in Godot…it's just that good, even with some weird C# issues.