Godot Essentials

The biggest lessons I learned in my first Godot project.

April 21, 2024
I chose Godot as the game engine for my last project, instead of Unity3D, the engine I have been using for the last five years. This was my first Godot project ever.
I learned many things during the process; below are the ones I consider the most essential parts for a Godot project as an amateur Unity3D developer. Please forgive me for skipping the details; I aim to focus on the "what" and "why" instead of the "how" in this article.

Think in composition

The most basic building block in Godot is node . It is the equivalent of component in Unity3D. There are many types of node, each with different function and purpose. A node can contain another node to make a node tree. This tree of nodes is called scene, the equivalent of Prefab in Unity3D. During development you may create enemy scenes, item scenes, platform scenes, and so on.
Now assume you want to make two slightly different enemy. Say an Orc warrior and an Orc chief. They are mostly the same, but the Orc chief has an additional ability.
One obvious approach is inheritance. Make an Enemy class for the common enemy logic such as AI or hitbox, then an Orc class to inherit it, and an OrcChief class to inherit Orc. The additional ability is implemented on OrcChief or as an independent ability class.
With Godot node, make a node for the ability, add it to the OrcChief scene, and it's done. Add the ability node to any other enemy scene and they have the ability too. It's also possible to attach or detach nodes in runtime, makes it easy to dynamically adjust enemy power without instancing a new one.
Applying the Single Responsibility Principle is natural with Godot's modular design. As a complex system is divided into several nodes, the boundary of each part of logic becomes clear. Node system visualizes the abstract borderline between components, massively saving cognition cost during development.
It was an "Eureka!" moment when I figured out this. The development became way easier and smoother after that.

Node group for quick access

Since system is divided into nodes, accessing nodes from another node is unavoidable. There are many ways to achieve this, one is through node groups. The groups system works like tag: a node can be assigned to as many groups as you want, and can be accessed from group name with the get_nodes_in_group or get_fist_node_in_group API.
Group is especially handy for accessing remote nodes: nodes that are not in the same scene with the invoker node, which makes it difficult to access by nodes' relative path. It also decouples the relation between the caller and the receiver(s), since the access is through group instead of direct reference.
I adapted this two months into the development; it was two months too late.

Signals to decouple

Another essential method to access remote nodes is through signals. It is Godot's interpretation of the Observer pattern.
It only takes one line of code to set up a signal to trigger a function, and one line to emit a signal. It is even code-less to bind a built-in node's event to a function in script. Say you want to detect units entering an area, just double click the body_entered event from inspector, press Connect and it's done.
I appreciate Godot's effort to make the signal system easy to use. The explicit guidance saves us new developers' huge amount of time.

Mouse filter, root of GUI interaction

I don't have proof, but I reckon every new Godot developer once struggled against UI and mouse interaction priority. Godot's approach to this is the Mouse filter.
When a mouse interacts with a Control node -- the base node for all GUI controls -- the mouse interaction event is always registered in the lowest level in the node tree. For example, if the node tree from top to down is panel node > container node > button node, the click event will first fire on the button. Depending on the button's mouse filter setting, it may handle the event, ignore the event, or pass the event to its parent.
If you find your UI not triggered when there is nothing wrong with the code, it may because its child node's mouse filter is set to stop which makes the node intercepting the event, where it should be pass or ignore.
The mouse filter setting is hidden in every Control node's inspector along with many other properties, but it is a critical part of how UI works in Godot. I wish Godot document emphasizes more on it.

Themes for extensive styling

Adjusting a single GUI node's appearance in Godot is not as straightforward as it is in Unity3D. Godot prefers styling through a theme and applying it to the nodes, instead of adjust nodes' style separately. It is a trade off of efficiency between project-wise style setting and single node style setting.
At first I was confused because even finding the style setting panels took some effort. But I expect the theme system thrives in extensive style settings, and I look forward to it.