Understanding C# in Godot
Intro
Just like promised we're going to explain what is going on with all that ceremony in those C# scripts. This isn’t a full C# course however! We’ll only explain the code written until now in this tutorial. We'll explain more C# stuff on later pages as we go when necessary. That should keep things intresting & fun without overwhelming you with the boring stuff.
To get started you can take a look at either Intro.cs
or Main.cs
.
It doesn't matter which one you take as they're both similar in structure right
now. In this page, however we'll take Main.cs
as an example to explain.
'Using statements' & namespaces
At the very top of your script, you’ll see something like:
This line just tells your code, “I want to use Godot’s built-in tools inside.”
You’ll see this at the top of almost every script in this tutorial.
One such example of a usage is the GD.Print
method you used to print a message
to the VSCode or the Godot console.
In C# terms, this is called using a namespace
. In fact you can also create
your own namespaces to use in other files like this, but that's a topic for
another time.
You can also skip using namespace
/using
statements, but the downside is
that you have to repeat the namepsace for each item you're using. For example
Node
becomes Godot.Node
& GD.Print
becomes Godot.GD.Print
:
Quiz: check the above script
Do you see anything that doesn't seem to be in the original?
Take a moment to compare the original with our version without the using
statements. You can also copy paste the above content in Main.cs
& use
Git
in VSCode
to see what's changed.
If you noticed the change but didn't understand the why that's ok. Just read
the next hint.
Unnecessary usings statements
What you also see is a using System;
statement. This line tells Godot you
also want to use some built-in tools from C#/DotNet.
This line is however greyed out by VSCode, since we didn't make use of
anything from that set.
Inheritance
The next line in our script is this one:
Here there are multiple things going on. The most important one is this one:
or something like this if you still have the using
statements:
What this basically tells Godot is that this is a class
(e.g. a Godot script component) for our Main
node & that is a subtype of
Node
. You can also compare this somewhat to real world hierarchies. For
example Tuna
is a Fish
& Fish
is an Animal
. The name in our script
doesn't matter however, since Godot attaches scripts based on their file rather
than the script content. For clarity however it's best practice to keep those
names consistent.
Class accesibility modifiers
The other 2 things in the previously mentioned line are public
& partial
.
As for public
you can simply remove it. What this is meant to do is to make
your class
accesible from other projects. This is very handy when writing
something similar to Godot
classes which you want to use for multiple game
projects. Removing this keyword implies to C# that you don't care about the
world outside your project & this class
is simply an internal
thing. In
other words
Another use case for the public
keyword is accesibility from test projects,
but we'll discuss that on later pages.
As for the partial
keyword its something that Godot expects in order to add
some additional logic to your class
behind the scenes. This keyword is usually
combined with something called source generators & is very handy when you're
dealing with lots of repetitions that can be automated through a tool.
There are also other use cases like chopping very large files into smaller
pieces without breaking the logic. For example, we could split Main.cs
like
this:
// Main.Startup.cs:
public partial class Main : Godot.Node
{
public override void _Ready()
{
GD.Print("Main scene started ...");
}
}
That is usually considered a bad practice however, since its not a logical way to split up your code. What should be done instead is defining other classes that do split up the work into multiple smaller parts.
Currently however, we don't have a lot going on so it's too early to think about
that. Just know that the partial
keyword is an essential part of a Godot
script class
& that Godot won't run without it.
Overriding inherited methods
The next thing on our list is the override
keyword. This keyword tells C# that
you want your own version of a specific method defined by the parent class. In
our case that would be the _Ready
& _Process
method of the Node
class.
This is something we can also use when defining our own classes. C# is very
strict about which methods can be overridden however. Unless a method isn't
virtual
it's not possible to override it. The Node
class for example had to
define the _Ready
& _Process
methods somewhere something like this:
Quiz: can you define a better method than _Process
Like we discussed earlier _Process
doesn't fully cover the purpose behind
it. Can you use what you learned about inheritance & overriding methods to
define a parent class with a better name?
Return types & parameter types
Let's look at this line again:
There are 3 more things here:
- Return type: which in this case is
void
- Parameter type: which in this case is
double
- Paramater name: which in this case is
delta
A return type in C# is often used to return the result of a calculation or
some kind of status report. In our case however we don't have anything to return,
so the return type is void
. That's also how the _Process
& _Ready
methods
are defined in the Node
class of Godot. This also means you can not change
this without breaking your Godot game.
The parameter type can be anything from a number type to a reference to
another class
. In our case its a number of type double
meaning its a
floating point number like 2.5 (2 and a half). Since this type was defined by
Godot in the Node
class, you can't change this one either.
Lastly, you have the parameter name delta
, which is how the parameter
inside the _Process
method is named. Unlike the other 2 things, you can
change the name delta
to whatever you want. That is because C# doesn't usually
care (see the warning below) about the parameter name. It looks at the type
instead.
This means we can even get rid of the comment explaining delta
by simply
renaming it to something more descriptive like elapsedTimeSincePreviousFrame
.
Just for your understanding, a method can also be defined like this:
To use this you can do something like this:
Named parameters
Earlier we said that C# doesn't usually care about parameter names & looks at the types instead. That’s still true, unless you use something called a named parameter when calling a method.
For example, our CalculateSum
method could also be called like this:
CalculateSum(number1: 2.5, number2: 1.5)
This is called a named parameter. It may seem helpful when a method has several values that are hard to tell apart, like:
CalculateDiscount(123, 19, true)
vs
CalculateDiscount(basePrice: 123, vatPercentage: 19, shouldLogSteps: true)
That second version is easier to read, but using named parameters is often just a quick fix to avoid improving the method itself.
In practice, overusing named parameters can lead to fragile code, especially if someone later renames the parameters in the method. It might look like a clean solution, but it’s often just a quick fix when the method design could use more work.
Don’t worry too much about that for now. Just be aware that named parameters are mostly used to make things look more readable. This is often not the best solution & there are better solutions which we'll explore later.
Quiz: can you avoid named parameters in the above examples?
Until this point you have had enough info to use one simple solution to
avoid named parameters. Suppose you would encounter something like
CalculateDiscount(123, 19, true)
in some point of your career. What can
you do with your current skill to avoid using that, but still keep the code
clear? If you need a hint, you may want to look at the CalculateSum
example right before the named parameters warning!
Method accessibility modifiers
By looking at this line again, you also notice the public
keyword present
here:
This is because just like with classes, methods in C# can have accessibility
modifiers such as public
, private
, or internal
.
These words tell C# who is allowed to use the method:
- public: Anyone can use this method, even from other scripts or projects.
- private: Only code inside the same class can use this method.
- internal: Only code inside the same project can use this method (this is the default if you don't write anything).
There is a lot more to cover, but that is enough for this page. In the upcoming pages we're going to apply some stuff we learned here as well as some additional configuration to further simplify the workflow. After that, we’ll return to the intro scene and continue working toward the final game logic and setup, including some modeling steps along the way.
This is a work in progress
I still need to write those parts yet, so be patient with me and come back on a regular basis to see if those parts are there yet