VV Eekly Update #11 - Godot Patterns
Hello everybody, and welcome back to the VV Eekly Update! Like I mentioned in last week's post, we're getting to the slog of game development where instead of starting a fun new project, we're having to actually FINISH a project. Imagine that!
Therefore, I don't have too much to show off this week. That's why I thought it would be a good time to dive back into the nitty-gritty of coding in GDScript (Godot's native language), and show off some coding patterns that I've found useful!
Godot Structure
I've found Godot to be fairly useful, and that it provides just enough baseline power that I can develop how I want to develop. In particular, there's a few coding patterns that I've found to be very useful throughout my entire project! They're not quite obvious enough to people new to Godot, so I'd like to share them here.
Even if you're not familiar with Godot, I can tell you about two basic concepts about Godot that will be useful to understand these tips.
First: Godot projects are structured as a tree of nodes. Generally, there is a "main" root node, which then holds all of the player information, enemy information, map information, etc. This organization system works very well to structure your game and to make your game logic as separable (and bug-free!) as possible.
Second: Godot's main game loop is single-threaded. This means that all of your code is run sequentially on just one thread, rather than attempting to all run at the same time in different threads. For each frame, the player updates and moves a little bit, then enemy 1 updates and moves a little bit, then enemy 2 updates and moves a little bit, then your health UI updates, etc.
These things together are great for keeping your logic as simple as possible, which generally means as bug-free as possible. However, both of these properties raise a question: what if you need to get information from one place or time to another place or time?
Singletons
The first useful pattern is to have a global singleton. In Godot, these are also known as autoloads.
Godot's scene system, while powerful and flexible, has a drawback: there is no method for storing information (e.g. a player's score or inventory) that is needed by more than one scene.
The Singleton pattern is a useful tool for solving the common use case where you need to store persistent information between scenes.
https://docs.godotengine.org/en/stable/tutorials/scripting/singletons_autoload.html
Singletons are both useful and dangerous. Obviously, they're useful in that they can get information from one place to another. Both your player node and your inventory UI node can have access to the player health.
However, they're dangerous in that they can be easily overused. If your player node and your inventory UI node and every enemy node and each item node and some random node you added for a hack has access to the player health, then it becomes much more difficult to track how the health gets updated. This becomes a lot more bug-prone and difficult to manage!
Therefore, the Godot-specific pattern is to have an event bus singleton.
We use a pattern on almost every project at GDQuest: a singleton that only emits signals.
It allows one node to react to a global event in the game or an event triggered by a node that’s very far from it in the node tree. And that without having to connect the two nodes directly.
https://www.gdquest.com/tutorial/godot/design-patterns/event-bus-singleton/
Think of Godot signals like subscriptions to an email list. Anyone who knows the email list address can subscribe to whatever gets sent, and, likewise, anyone who knows the email list address can send something to whoever is subscribed. However, no one needs to know the entire list of people who've subscribed, nor does anyone need to know who can send messages. It's a great way to de-couple the "who's subscribed" from the "who's sending".
I want to go further than the linked article (and most other resources online about event buses) and recommend that event bus singletons be mostly used for UI changes.
Almost necessarily, a Godot project will strongly separate gameplay nodes and UI nodes. The root node will almost always have at least two children: a game parent node that holds all gameplay-related nodes and a UI overlay parent node that needs to be visible over all of the gameplay. I have found that for 95% of the time an event bus singleton would be useful, it was useful for UI changes.
Wait for set
The other useful pattern I want to talk about is what I call "wait for set". Here's what it looks like:
# WHERE PLAYER NUM IS DEFINED
var player_num := PlayerNum.UNSET :
set(val):
player_num = val
player_num_set.emit()
signal player_num_set()
# EVERYWHERE PLAYER NUM IS ACCESSED
if player_num == PlayerNum.UNSET:
await player_num_set
do_something(player_num)
In general, the pattern is:
Initialize a variable to an unset value.
If the variable gets set, emit a special signal.
Wherever the variable gets accessed, check if it's the unset value.
If it is the unset value, then wait for it to be set properly.
Then you can do something with the variable!
There are MANY reasons why a variable might be unset initially. Sometimes, you need to wait for somewhere else in the tree to initialize, like if a player should know which room they're in but the map hasn't been initialized yet. Sometimes, you need to wait for a player to take an action, like when you ask a player to choose which color they want to be. Sometimes, you need to wait for an internal RPC, like when the server needs to randomize the map and you're the client waiting for that to happen. Sometimes, you wait for an external RPC, like when you ask Steam for the player's persona name on Steam.
This trick works well because Godot's main loop is single-threaded. If the game loop was multi-threaded, then you would need to work more directly with mutexes (which are supported! but harder to use).
Alright, I think that's enough Godot tricks for today! Please feel free to send any thoughts you have into the event bus Discord singleton, and see you next week!
The post VV Eekly Update #11 – Godot Patterns first appeared on Shrubb Games.