Ludum Dare 50 has been the most fun I’ve had in a game jam in quite a while. Leading up to the event, I had decided that the instead of participating in the 48 hour Compo format as usual, I would try out the new Extra format.
Almost immediately, I realized I wanted to keep working on this idea after the Ludum Dare event. So instead of rushing to get a game out, I took my time working on the core game loop. As a result, the final product is more of a tech demo than an actual game, but I’m really excited about where I can take this in the future.
April 3, 2022
On Friday evening, I got home from work and sat down to have a quick dinner right as the theme was announced: Delay the inevitable.
After brainstorming for just a few minutes, I came up with an idea that stuck, and latched onto it. I would make a game where the player is alone in the woods, with a slowly dying fire, and they would have to feed it firewood to keep the flames alive as long as possible. There would be no win condition per se - your metric for success would just be how long you can keep the fire going before it inevitably burns out.
Great! That seems like a solid idea. Next challenge: what should the gameplay look like?
For years, I’ve been wanting to make a game on a hexagonal grid, but never had any ideas of what the actual game would be. This seemed like a good time to try that idea out. The player could occupy a single hexagon tile at a time and search out tiles that had a wood piece to collect. Once they have enough pieces of wood, or have strayed too far from the fire, they can make the journey back to deposit the firewood to the campfire.
Since I was going with discrete movement on a hexagon grid, I also wanted to take visual and audio inspiration from board games. It felt like a natural fit with the gameplay, plus it would make the models and sound assets easier to create on a tight deadline.
Since hex grid movement was going to be the foundation for this game, I figured starting with the hexagon tiles themselves was a natural starting point. I hopped off to Blender to whip up some low poly hexagons and got them imported into Unity.
This was my first attempt at a hexagon grid layout. The math was messy, but I got it working pretty quickly.
Once I was happy with the grid layout, I created a simple player token and coded some movement behavior. I was exhausted at this point, so once I had the proof of concept working, I headed to bed.
No color yet, but we have movement!
I spent the entirety of next morning working on audio. Continuing with the board game theme, I wanted the sounds to give the game a very tactile feeling and response while playing. I experimented with dropping a variety of household objects onto different surfaces until I got the sounds I liked.
This rock, Rubik’s cube, and bamboo tray were the stars of the show.
I also needed some color on my assets. I briefly considered going for a painted wood look. While it would have been thematically consistent, it didn’t seem like I was going to get good looking results quickly enough. Instead, I used a shader that I wrote last year for my game Hedge Maze. It does a simple blend of 4 colors depending on the direction the surface of the object is facing: Top, Front, Side, or Bottom.
Here you can see how changing the Top and Front colors affects the tile model.
I also spent a lot of time modeling and animating a campfire, which didn’t work out very well. It had been a while since I had worked with animations in Blender and Unity, and I fought for a long time to get several animations for the player token, tile, and campfire working in the game. Ultimately, I had to cut my losses and decided to animate everything in code instead. This burned up a lot of my Saturday.
Sunday was a bit of a wash as well. I spent a lot of time working on a visibility mechanic, where tiles would be hidden if you were too far away from them. One thing that I did achieve was a board that generates new tiles as you explore. I am really pleased with the effect!
Unfortunately, that’s where I had to stop for the weekend. I was not able to get the firewood mechanic implemented, which is supposed to be the core of the game. Thank goodness I have 3 more weeks!
Controls: QWE, ASD to move.
April 9, 2022
After recovering from the jam weekend, I needed to plan out what I was going to spend the next couple of weeks doing. I really felt like I’d come up with an interesting idea for a project that I would want to keep working on, so instead of trying to rush out a quick playable game, I decided to take my time and see how far I could get in the next week working on a solid foundation for future work.
Of course, that meant I deleted all my code and rewrote it all from scratch.
See, the problem with my jam code (and I expect a lot of others have this issue too) is that it’s written to get quick results on screen, but it’s not scalable by any means. Speed comes at the cost of flexibility and stability, and it really leaves your project in such a messy state that it would be difficult to work with it for too long.
The first thing that I knew I needed to do was rewrite the hex coordinate system. My first implementation was rushed and naive. I treated the hexagons as a 2d grid of squares; each column offset by half of the former.
This seemed simple… at first.
The problem with laying out hexagons this way is that it makes a lot of simple operations needlessly difficult.
Because each column is offset by half, your
Y coordinate is always dependent on whether your
X coordinate is even
I needed a better solution.
A year or two ago, I had bookmarked a webpage: Hexagonal Grids: from Red Blob Games. Specifically, there is a section on Cube Coordinates. I’ll be the first to admit that it took a while to wrap my head around the idea, but once it clicked, the math behind the grid system became way simpler.
The link above explains this much better than I ever could, but the basic idea is to give each cell 3 coordinate values:
S. Each step to a neighboring hexagon changes exactly two of the coordinates.
With that figured out, I also needed to work on the gameplay loop. I was really starting to think about the gameplay in board game terms - where each round, each player takes a turn that consists of 1 or more actions.
This is overkill for the jam version of the game, but I wanted to lay the groundwork for multiple active players in the future.
Unity’s coroutine system is a really handy way to build this loop - it lets us step from action to action and wait for player input, while still allowing things in the main update and render loops to continue uninterrupted. If you’re curious about the implementation, check out GameManager.cs on GitHub.
The last important thing I needed to do was break up my components. I had a few bloated components that were handling all aspects of the game. Keeping scalability in mind, and also trying to obey the single-responsibility principle, I needed to separate my components into smaller pieces that did just one thing each.
Probably the most important part of that was separating the pure logical implementation of the data models that the game deals with from their physical representation on the screen. To that end, I split everything into a separate “Brain” and “Body”. The brain would handle all the logic of the game, and then it would send update events to the body so that it can show a visual representation of what had just happened.
For example - an
Actor represents an entity on the board that can take an action (such as the player), and the
is the 3D model that represents that entity. So when we want to move, we call
Actor.Move(), which will update the
actor’s position. At the same time, the actor also calls
Token.Move(), which will play the animation and move the
piece on the board.
Here are a few examples of the Brain-Body relationship in the game.
After all that work, the game ended up playing exactly the same. No new features were added, but I had a much more robust foundation for the game.
Controls: QWE, ASD to move.
April 16, 2022
As the last weekend loomed, I knew I had to take a minute to figure out what the end product of the jam would actually look like.
I had my mind set on working slowly, but at the same time, I wanted something playable with a definite start and finish. Even if the end result would be more of a tech demo than a game, it should still have gameplay elements that are obvious to anyone giving it a shot.
This is a game jam after all. It feels wrong not to have some semblance of a game.
There’s not much to write about at this point - most of the code written in this last sprint was quick and will be discarded when I do my post-jam cleanup. I did more work on the visibility mechanic, and threw together a quick system to randomly distribute wood as you explore the board.
The intro and outro do an okay job at bookending the experience and making it feel like a game: you know your goal and inputs moments after booting up, and the number of rounds you are able to last serves as a score mechanic.
One thing that I am a little proud of is the animation of the board collapsing when you finally run out of turns.
Believe it or not, this took all of 5 minutes to implement.
At that point, I decided that this was an appropriate stopping point for the jam.
Thoughts on Ludum Dare Extra
I’ll echo my sentiments from the beginning of this post: This is the most fun I’ve had with Ludum Dare in a long while. The Extra format gave me time to work on a fun prototype and still live my life. And more importantly, it transformed the event from an exhausting, panic-filled weekend into an inspired weeks-long journey of creativity and exploration.
In the past, I would have viewed this final product as a sort of failure. It has some clever ideas, but it isn’t particularly fun, even through the generous lens that we view jam games through.
But I did what I set out to do. I started working on a new game, and had a blast doing it. I have so many ideas for where I can take this project in the future, and I can’t wait to share it!