Post-mortem retrospective of my golfing game

Intro

I made a cute and crazy golfing game for the Ludum Dare 57! I think I managed to build pretty cool game ideas and mechanics, based on the "depths" theme. This was over a month ago. Yeah… it took me a month of procrastination to finally sit down and write this. But better late than never, here we go!

Game jam

I couldn't sleep well the night before the Ludum Dare began, so I had some time to think about what kind of game I wanted to make for the theme. I usually spend about the first half of the first day thinking about game ideas. But this time it was actually quite quick. Of course, the first ideas that came to my mind were games about caves, mining, and fishing. While I love these kinds of games (Minecraft, Terraria ❤️‍🔥), I wanted to do something more original.

I got the idea to make a golfing game, but each hole would lead into the next level. I had never made a golfing game before, so this was… seductive! It would also be about platforming since the player would always have to get to the ball physically, and also get to the hole in the end to progress to the next level. I love platformers, so another plus point for this idea.

BTW, if you're interested in caves and mining just like I am, here's one amazing video I recently saw about a mining game made for this Ludum Dare. I love how it looks, works, and feels! Check it out: How I made a procedural cave game in 48 hours | Ludum Dare 57 Devlog

I immediately jumped into making the game in the morning. I prototyped the golfing physics and having the player hit the ball. I drew a (quite nice, IMO) sprite and animations for the player and the ball. The first level is just on the ground, so I focused on making it work on the first day. I made a tree, implemented the golfing mechanics like hitting the pole and going to the next level. I also made a simple system for the camera, which tries to keep the player, the ball, and the current pole in view, while the player always gets priority. In the evening, I added the first sound effects (more about those later).

ffe9786b-4a76-4561-b51c-7fd3453f2322.png

On the second day, I finished the second level and worked a lot on the systems of chaining levels in the game. ⛳ I polished the first level, made a few more levels, added some juice and effects, better aiming, more sounds, and improved the physics. I added a scoring system and a simple menu. Finally, I added the last "end" level, packed everything, and submitted it to the compo version of Ludum Dare. Phew!

image.png

Planning

There’s usually not much time, nor point, for planning during a game jam. I just drew a couple of my ideas and kept a simple checklist of things I wanted for the game. I usually do it like this for game jams. Here’s how it all looked for Down Golf:

planning.jpg

Tech

I made this game, as usual, in Godot. 🤖 I used Aseprite to draw the sprites and I recorded my own sound effects for the very first time. It was a lot of fun! No special microphone or anything, just my iPhone.

I originally wanted to also include some music and I would have used Ableton for that. But as usual, I did not have enough time. I always put sounds and music as the last priority and then almost never get to them. 🔇

Other than that, Git as usual, and I used Itch.io’s Butler to publish new builds easily and often.

Constructing levels

Okay, let's start with the game levels. The game works like this — when the player gets the ball to the hole, it opens up and lets the player pass through, going down into the next level. That means that all of the levels are connected (chained) in one big "main" scene.

In Godot, this is actually pretty easy to do. I have one big scene called "main" which contains all the levels, the player character, the ball, and the camera. The levels are positioned in a way so they connect correctly.

CleanShotem2025-05-06/emat_17.01.55.png

Each level is a separate scene so I could work on them separately. I also needed a way to run and test each level independently, so I would not have to rerun the whole game to get to the level I’m currently working on. I constructed each level from smaller objects or components which are all independent on the rest of the game. I heavily use Godot’s signals, too. Here’s how a level looks:

CleanShotem2025-05-06/emat_16.58.55.png

Here we go a little into the technical details. If you are not interested in the implementation, you can skip ahead. You can see that each level is sort of made from small LEGO pieces. 📦 The spawner is what ensures that each level works correctly when it is run either in the main scene or separately on its own. When it loads, it checks whether there is a player in the game, and when not — which is only when the level is run on its own — it spawns the player and the ball at its position. In the main scene with all the levels, the player and ball are already there, so the spawner does nothing.

CleanShotem2025-05-06/emat_17.03.34.png

Other LEGO pieces work similarly. I won’t go into too many details here. The pole tracks whether it is “active”, and when it is, it counts how many shots the player takes in the current level. It does this by hooking into the player’s signal ball_hit.

CleanShotem2025-05-06/emat_17.08.01.png

CleanShotem2025-05-06/emat_17.08.09.png

You can see that I rely heavily on groups and @export variables. I love writing scripts this way, it keeps them independent of each other and the scene structure. You will see a similar theme with the game objects (described below). 👀

If you are curious, check out the full source code of the game on GitHub: https://github.com/martindzejky/down-golf

You may be curious how all this connects in the "main" scene, the game. Well, it's actually very simple. Essentially, when a level is completed, it emits signals ball_entered_hole and player_entered_hole. I connect these signals between the levels in the editor, which means that when one level emits these signals, it calls the "activate" function in the next level.

What I could have done better here is that, instead of placing and connecting the levels manually in the editor myself, I could have created a "manager" object with an array of levels. It would place and connect the levels automatically based on their "enter" and "exit" points, so they would be chained nicely. This would have also allowed me to load one level at a time and to allow the player to restart only the current level, instead of the whole game like it is right now.

All in all, I'm really proud of how I structured the levels. They are flexible, independent, and easily testable on their own. 🎉

Game objects

In Godot, all game objects are just scenes — trees of nodes. I think the best practice is to try to use as many small nodes to implement object behavior as possible, instead of doing everything in script. This is called "declarative" programming, rather than "imperative". Using nodes, you "describe" the behavior of the game object and configure and connect the nodes to do that, instead of coding everything yourself.

For example, this means using Timer nodes to implement waiting a fixed time, instead of doing that in code; or heavily using the AnimationPlayer node to control all animations and scripted behaviors. Did you know that an animation in Godot can control any other parameter of any other node, as well as call any function during the animation? 🤯 Very powerful!

Here's the player object as an example. Notice how many nodes it consists of.

CleanShotem2025-05-07/emat_10.10.23.png

I also needed to have a simple state machine for the player, because the player can be moving around, aiming, or shooting the ball. These states are mutually exclusive. I would usually also do this with nodes, but here for simplicity I just used a single enum and a variable current_state in the script, along with a fairly small match (switch) statement.

CleanShotem2025-05-07/emat_10.12.34.png

CleanShotem2025-05-07/emat_10.12.45.png

The ball is similarly very simple. It's basically a rigid-body physics object that displays particles and plays sounds when it collides with anything.

CleanShotem2025-05-07/emat_10.14.08.png

One last example. Here's the pole and its nodes. ⛳ Its script is also fairly simple, it just detects when the ball or the player enters the hole, and emits appropriate signals. It plays an animation, displays particles, and plays sounds. That's all.

CleanShotem2025-05-07/emat_10.16.06.png

Notice that all the objects are independent of each other. For example, the ball object does not use any external state or other objects. The pole is detecting collisions using its area colliders and collision groups. It does need to know when the player hits the ball, and I'm using groups and signals for these kinds of soft dependencies. It works very well and I don't have to connect the objects in any way in the editor.

CleanShotem2025-05-06/emat_17.08.01.png

CleanShotem2025-05-06/emat_17.08.09.png

Using groups is an excellent way to work with game objects in Godot. For example, I always have a "player" group and there's usually just a single player in the game, so it is easy to get access to it from anywhere using get_tree().get_first_node_in_group('player'). Similarly, all poles in the game are in the "pole" group, and it is very easy to look up or even deactivate all poles in the game at once using get_tree().call_group('pole', 'make_inactive').

Using signals is another Godot way of hooking up game objects. 📣 It ensures that, for instance in my game, the pole game object does not have to care how ball hitting is made in the player object. It just uses the ball_hit signal, that's all it cares about, and then it is the responsibility of the player object to implement and emit the signal. Simple inputs and outputs, which I always like to work with!

Again, I won't go into more detail here, but if you are interested, check out the full source code of the game: https://github.com/martindzejky/down-golf

Graphics

As I've mentioned, I drew the sprite and animations of the player character first. I also drew the ball, the pole with the flag, and the tree in the very first level. It took me a lot of time, actually, but I think the first level in the game is the best. It is close to the style that I envisioned for the game. I'm proud of what I achieved here. 🌳

image.png

The other levels, unfortunately, did not receive as much attention, and as a result, they look very blank and boring. I was focusing on implementing the game mechanics pretty much for the whole time, so I did not have much time (nor ideas) for how I would like the underground levels to look. All the levels pretty much look the same. 😵‍💫

image 2.png

I learned once again during this game jam that I’m a great programmer but not a great artist. I can implement game mechanics quite quickly and efficiently, but I spend way too much time on art, and the result is not that amazing. 🎨 Others have also noticed this:

CleanShotem2025-05-06/emat_15.58.44.png

CleanShotem2025-05-06/emat_16.00.40.png

CleanShotem2025-05-06/emat_16.03.16.png

What can I do about this next time? Well, I can either team up with an artist — which I’ve never done before so that would be an interesting new experience! 🎨 — or really focus on making games with great mechanics and super simple graphics. It can be done and there are a lot of successful indie games out there with very simple graphics.

I also recently watched a video about using vector art instead of pixel art for games: https://www.youtube.com/watch?v=rKt95peINyk It got me really curious! I’ve never really tried that and it might be worth it for some future game jam as an experiment. In general, I’ve always defaulted to pixel art for my games. I would like to try some other simple styles of visuals, too.

Audio

For the very first time, I did not use Bfxr/Sfxr or anything similar for generating sound effects! I instead used… a microphone and my own mouth. 🎙️ I recorded hilarious sound effects for hitting the ball, steps, jumping, the trampoline… The sounds in this game add so much charm to it and honestly make it what it is.

I had a lot of fun recording these. We laughed so much with my girlfriend when I was making weird hit sounds into my mobile phone! 😆 This was honestly one of the best parts of this game jam for me.

That's where it ends, though, for audio. I wish I had recorded and added some small ambience sounds. That would have added a lot of depth to the levels. There's also no music — I just did not have the time and I did not want to just slap in some AI-generated track (which isn't even allowed in Ludum Dare AFAIK).

Posting videos

Another "first time!" While I was working on the game, I made and posted several short-form videos about the game and my time in the jam. This was an entirely new experience for me — I've never really made videos before, especially ones where I talk to the camera! This was a very valuable experience, and I will make sure to do this for all of my future jams since it was so amazing. It also brought a lot more visibility to the game, and some of my friends were following the development closely. Some are still poking me today about where the next video and the next game are… 😬 Again, such a great experience!

Check out all the videos about the game over here:

Down Golf ⛳️

Lessons learned

Okay, so what did I learn on this project? Well, I really think I have a good game idea here. Lots of people played the game and liked it, and I got a ton of positive feedback about it. I think it's a unique idea and a good mash of genres. It could be worth making it into a "full" game — what do you think?

Sound effects were a big win in this project. This was the very first time I tried making and recording my own sounds, and it paid off big time. It actually defines the game, IMO! If I ever turn this into a "full" version, it still has to use these sound effects! 😆

Graphics were a fail, I think. I spent a lot of time on the few important sprites in the first level and the main character and ball sprites. They look good, but the rest of the game then looks terrible. I could have instead focused on using very simple, even primitive graphics, so I could focus more on the other parts of the game. That being said, the main player character does have charm, so perhaps it wasn't a waste of time.

What caught me by surprise is that the physics are inconsistent. I have no idea whether this is Godot's problem or I'm doing something wrong in my own code. The ball is basically using the built-in Godot 2D physics — nothing special. I'm just applying an impulse when the player hits the ball — the impulse is always applied at the center of the ball. And yet, sometimes the ball seems to fly in a different direction than the player was aiming for. This happens especially often when the ball is on a slope. I will have to do more experimentation here (an upcoming video? 😏)… Although to be fair, I don't have that much experience with 2D physics.

Another big fail is that I did not leave myself enough time for adding more mechanics. The platforming aspect of the game is especially dry; there's not much to do aside from just jumping. I did manage to implement trampolines into the game and they are so much fun. But I had a lot more ideas for how to make the golfing levels more interesting.

On the other hand, I really enjoyed working with Godot again and using "clean" systems to implement the mechanics. I love that the levels are self-contained and isolated from each other, and I love how objects are made from simple components. It was very enjoyable to work with and easy to tweak and change when I needed. 😌

Overall, I had a lot of fun with this project. I enjoyed what I'm good at — programming and development of game systems — and I added 2 new things which I truly enjoyed — my own sounds and making videos. At the same time, I struggled with the same things I usually struggle with — no music, wasting time with not great graphics, and focusing too much on pretty and clean code rather than implementing a lot more game mechanics which would make the game more interesting.

Outro

Thank you very much for reading this retrospective! It was great for me to finally sit down and write this, even though I was procrastinating on it for a month. 😶‍🌫️

If you haven’t yet played the game, check it out here: https://martindzejky.itch.io/down-golf The Ludum Dare submission page with comments is right here: https://ldjam.com/events/ludum-dare/57/down-golf

Please let me know if anything interested you in this article, or if you have any comments or questions. I’ll be happy to reply!

My next step is to make a long-form YouTube video out of this retrospective. 📽️ Wish me luck — I’ve never made this before. Then, I’m eager to jump into a new game project. Or, who knows, continue working on this one and exploring more ideas? Let me know if you would be interested.