This content originally appeared on DEV Community and was authored by Martin Grignard
On a quest to learn about game development with the Godot Engine, I recently took on the 20 Games Challenge. This whole challenge revolves around the idea that finishing a series of small scoped projects of increasing complexity is better than not finishing a Dream Game.
“Done is better than perfect.”
– Sheryl Sandberg
In this article, I present how I finished my first game ever1 while respecting the following constraints:
- “20 hours […] as a starting time limit”: This one is pretty self-explanatory.
- “No tutorials allowed”: I extended this one to “No LLM allowed”. My goal here is to learn, not to copy-paste some code.
- “Push it as far as possible”: This is a self-inflicted one. The 20 Games Challenge provides a set of stretch goals for each game to push the scope and the difficulty further up a bit. I want to reach them while remaining under the time limit.
A matter of choice
For the ten first games, the challenge provides a selection of good candidates from which one can choose. The first two being Pong and Flappy Bird. I have always loved the story behind the latter: A game so addictive that its creator felt guilty enough to shut it down and remove it from the app stores, despite the massive ad revenues generated.
So… Flappy Bird it is!
A bee’s first flight
Thanks to Kenney’s assets, I quickly got all the assets needed to achieve this first game. After reading parts of the wonderful Godot documentation, I had a working version of Flappy Bee that met all the requirements but one: the game-over screen. I chose not to include it since I find the game more addictive when there is no real break in the gameplay.
In the following sections, I will discuss how I implemented some of the mechanics of the game. If you cannot wait to try it, feel free to skip to the end.
A story of physical attraction
The Godot engine comes with a series of 2D physics related components. In Flappy Bee, I used four of them:
-
StaticBody2D
: The world borders, used to detect when the bee is leaving the screen and when obstacles are out of bound, are nothing but static colliders. Hence, this component, coupled with aCollisionShape2D
setup with aWorldBoundaryShape2D
was exactly what I needed to model them. -
CharacterBody2D
: Well… What component would better fit the bee than this one? (Really though, if you think there is a better option, let me know in the comments) -
AnimatableBody2D
: Since they are “useful for moving platforms, doors, and other moving objects”, they were perfect for the obstacles which are just horizontally moving platforms. -
Area2D
: To increment the score whenever the bee passes through a hole between two obstacles, I needed a way to detect a collision between the bee and something that would not push on the bee when colliding with it. This component did just that.
A path strewn with obstacles
The most interesting part of this first challenge was to implement the obstacles and their spawning mechanism. To make the level infinite, I needed an easy way to instantiate new obstacles with randomly placed and sized along the Y-axis.
Therefore, I made great use of the @tool
and @export
decorators and built a parametric ObstaclePair
scene.
Then, I used it in an Obstacles
scene that is responsible for the difficulty management using both a hole_size
and spread
variables. While the hole_size
is easy to comprehend, the spread is used to constraint where a hole on the next obstacle can be located based on the current one. The greater the spread
value, the further the next hole can be on the Y-axis.
A waggle dance
Just like honey bees communicate together using a waggle dance, nodes in Godot communicate with signals. While this construct is very powerful and allows for event driven logic, connecting nodes located far apart in the scene tree can be tedious and fragile if done wrong.
To prevent adding hard coded, exported node paths or signal forwarding logic on multiple components of the game just to connect a few signals (e.g. when the bee passes through a hole), I defined an event bus using a singleton, or, as they are called in Godot, an autoload.
While I know that the singleton pattern is often referred to as an anti-pattern, it proved to be very useful in that setting.
Putting it all together
After assembling all these scenes into one, adding a few UI components and connecting them all together using the event bus, Flappy Bee was complete, ready to be played!
For the most curious out there, the sources for the game are available on Github. Feel free to give feedbacks here in the comments and to report any bug you encounter using an issue.
Footnotes
-
More than a decade ago, I tried to learn game development with Unity (It was all the fuss at that time) and started several projects, but never ever did I finish one. At that time, all I did was follow some tutorials and glue things together with horrendous spaghetti code. Then, I took on an engineering degree and left my game dev dreams behind for a while.
This content originally appeared on DEV Community and was authored by Martin Grignard