DaveWarnock.com

KenneyJam: Making Hotshot Tiler

While I’ve been toying with game development for a long time, I don’t have a pile of finished projects under my belt. This isn’t unusual for people who are interested in game development, and it’s part of the reason that game jams were created. Game jams are good for people who tend not to finish projects as they are forced to call some part of the work “done” before the time limit.

For those unfamiliar with the concept, a game jam is an event where small teams or individuals make a very small game within a set time limit. Game jams are usually themed, meaning that there are some constraints on what you can do. Some game jams are competitive, but most are just for fun.

I’d been interested in doing a game jam for a while but as I struggle to create assets, I hadn’t yet found a game jam that appealed to me. It was KenneyJam that changed this. Kenney is a prolific creator of public-domain game assets, and the core idea of KenneyJam was to only use assets he’d provided. No asset creation, no problem.

Preparation

Having never participated in a game jam before I joined the KenneyJam discord channel and sought out advice from people who had done these things before. The advice I got was pretty solid, and included:

  • Have some ‘seed’ ideas ready to go that can be adjusted to suit the theme.
  • Prepare re-usable components that you think you’re going to need ahead of time. The most obvious of these is a title screen, but could include path-finding algorithms, GUI elements, etc.
  • Know your tools and their limitations to ensure quick progress.
  • Take part in the community during the event, as it’s collaborative and helps you stay with it.
  • Take breaks and sleep at night. Plan out meals and breaks ahead of schedule. Have food ready so you can eat while you work.
  • Familiarise yourself with the assets so you know what’s available when you’re at the game design stage.
  • Make sure tools (e.g. Git repos) are set up and ready to go.
  • Always submit something well before the deadline, in case anything goes wrong.

I’d been itching for a chance to use LibGDX and decided this was the perfect opportunity to try it. After looking over the assets, I decided that the 2D roguelike assets were likely the ones I wanted to use. To make levels, I did some research and settled on the Tiled map editor.

With my tools decided, I spent some time reading the APIs and put together a simple title screen. I’d hoped to do more but life, as usual, had other ideas. When the time came to start work in earnest I had a good overview of what I needed to know, but very little experience with my tools. Ready or not however, my time was out, and it was time to start.

The Game Jam

Day 1

This wasn’t completely a solo effort. I had a professional tester on board and a friend interested in game design to help out with some minor things. I’d put together a chat room for us and waited for the theme announcement. The theme was “It’s a feature, not a bug”. I have to admit I was immediately stumped. I grabbed my phone and took the dog out for an hour to mull it over and form a plan of attack.

Because I’d been reading the API for LibGDX pretty closely I knew there was some clever stuff I could do with the tilemap itself. Slowly an idea started to form: I could make a map with Tiled and the Roguelike tilesets. I could import that map into a level, then give the player the ability to interact with the map itself, lifting tiles and moving them around. Bad guys, which I called Mooks on a whim, would try to get to the player and hurt them by touching them. But if the player can lift tiles, the Mooks would fall through the holes in the world and die. It’s a feature, not a bug. I also figured it made sense if the player could throw the things they picked up. I eventually settled on some basic rules for the mechanic.

  • The player can pick up tiles and NPCs, which will be held over their head.
  • The player can throw picked up-things.
    • NPCs will be hurt by being thrown, but will recover.
    • Tiles will be destroyed by being thrown.
    • Anything thrown will damage Mooks.
  • The player can place things down.
    • A placed tile will replace the tile it is placed on, if one exists in the same layer.
    • If no tile exists, placed NPCs will fall through the map and die.
    • If no tile exists, placed tiles will fill the hole and act as basemap tiles.

These simple rules allowed the formation of a system that allowed the player to reshape the level. They still need a “win” and “lose” condition but on the surface this was a good place to start.

I ran into some issues during development. The first problem I found was something I should have noticed - that the roguelike characters always face ‘south’ so it wasn’t clear what direction they were pointing in. Limited to only use Kenney’s assets, the solution came up with was to create a ‘cursor’ that would point at the appropriate tile. For lack of anything else to use, I used a skull. This actually worked really well.

Other issues I had included an alignment issue from the tilemap renderer. Some sort of rounding was going on when drawing the tiles, resulting in the background being visible around the tiles. I was able to fix this by locking the map to only move to positions that mapped to screen pixels. I thought this would result in some jumpiness, but it actually didn’t result in any jumpiness at all. Looking back this makes sense, as you’d only be able to perceive it relative to the pixels of the map, and as they’re not jumping whole pixel distances, all you do is eliminate the glitches.

Map alignment issues that were solved by rounding the map's position and scale to ensure whole coordinates were used.
Map alignment issues that were solved by rounding the map’s position and scale to ensure whole coordinates were used.

My buddy Andrew Dunn wrote a loopable track on the first day after I had nailed down a concept. We’d agreed on something glitchy and funky, and he played a little tune on his guitar then did some processing. It was what I needed, but I didn’t get it into the game until the second day.

The main theme for Hotshot Tiler.
Andrew Dunn

At the end of the first day I had the general systems up and running. I was able to pick up and place tiles, and the skull cursor had resolved the issues of all the sprites facing south. I was pretty pleased with my progress and called it a night.

Showing the tiling system working along with the skull cursor. No music yet, as this was towards the end of the first day.

Day 2

In the second day I knew I needed to add health, pathfinding and win/lose conditions. Health was easy enough to implement, but harder to show to the user. I wanted to put little health bars on things, but I discovered a quicker and easier way to do it. LibGDX provides a method to apply a tint to a sprite as a single operation. I’m not sure how it works, possibly just applying the colour with an alpha over the sprite, but it gave me a quick and easy way to represent health by fading the sprites into red. I was also able to tint them brown to represent them being dead.

I was also able to use the sprite tinting to create a ‘glitch’ effect on things that were picked up by tinting the sprite a random colour every frame. This was a cheap and effective visual trick, but I think it ended up being a little jarring in the end.

Video showing the glitch effect. At this point I’d put music in, but the pathfinding still wasn’t working.

There were a bunch of libraries for pathfinding, so I opted to use one of those for speed. Unfortunately, it didn’t quite work out that way. I was certain I’d implemented everything as described, but nothing was working in the game. After wasting a few hours looking through my code, I decided to dip into the pathfinding code itself and discovered the source of my woes; the pathfinder was checking if two coordinates were the same using a==b, rather than a.equals(b). The pathfinding system was using data from the actors, each of which has their own position instance, so of course nothing worked. I can’t remember the exact solution but rather than hacking a fix into the source of the pathfinding library I was able to ensure that the same coordinate instances were fed into the pathfinding library, as odd as that sounds. This immediately solved my problem and I had a pretty good A* pathfinding algorithm up and running. It worked, performance seemed great; the only issue is that it probably took me longer using a library than it would have taken to write the algorithm from scratch.

Pathfinding isn’t the whole AI picture of course. I had to add some goal-seeking behaviour, and that all tied into the win/lose conditions. I figured that the most fun thing would be to have a bunch of baddies (that I was calling mooks) try to kill a bunch of townsfolk. I set up the game to spawn a bunch of mooks and a bunch of townsfolk. The game then tracked their numbers. The win condition would be if the player killed all the mooks before the mooks killed the townsfolk. You lose either when you died or the mooks killed everyone else.

The mook AI was easy enough. The mooks would just “know” where the nearest opponent was, either townsfolk or the player, and would move towards them. Contact is enough to kill. The townsfolk, similarly, would know where the player was and try to move towards or away from the player to maintain a certain distance. They didn’t have any other brains and would not try to avoid the mooks. Their behaviour ended up being quite random and the elastic behaviour made them look much more intelligent than they really were. Both mooks and townsfolk made no attempt to avoid the holes in the map, so lifting up squares and trying to lure mooks in seemed to be a good strategy, but as the townsfolk would chase you it created a risk for them too.

While I was working on all these things, I was getting help from Andrew Dunn. I’d set him up with a laptop and the Tiled map editor, explained the purpose of the layers and let him do the level design. I had proposed an initial map size that was far too large however, and he struggled to fill it in the time available. We ended up with a little town on an island with some paths and forests, which I was able to import directly into my game.

Another person working away in the background was my friend Andrew Knotts, who was testing the game for me. I was delivering builds to him and getting some feedback on things that needed work. Having someone available to help with the testing and give feedback was a valuable asset.

Towards the end of the second day I had put all the pieces together and submitted a build ahead of schedule to itch.io. I didn’t have any marketing materials prepared, so I threw something together at the last minute. I made a few more builds after this, fixing bugs and issues. Ultimately I was happy with the result.

All the parts coming together. Shows the menu, AI, some stupid townsfolk falling into a hole…
The game always starts in front of this church.
The game always starts in front of this church.
As the game progresses, dead villagers will start to pile up.
As the game progresses, dead villagers will start to pile up.
Holes in the world are bright red.
Holes in the world are bright red.

Unfinished Work

There were a few things in progress that I wasn’t able to complete by the deadline. One of most notable was the lack of sound effects. Andrew Dunn had actually provided me with a range of sound effects to specifications that I’d given him. Unfortunately, as the deadline approached it was clear I wasn’t going to have the time to set up all the hooks needed to trigger them, so I dropped it and moved on. I also didn’t have any time to add things like screen-shake. This left the game without much in the way of ‘game feel’, so it felt pretty dry.

The other thing I wanted was more ‘glitch’ effects. Specifically to the background that’s visible when tiles are removed and to things held over your head. I was able to partially get this working with the random colour shifting on held objects, but what I’d wanted was a particle effect or maybe a basic shader that made things look like they were breaking apart.

The red background I had originally set to contrast with everything else. Towards the end I tried changing it to some other colours, but without any additional effects it wasn’t clear at all that it was “wrong”. In the end I restored the red background for submission.

Post-Mortem

I’m writing this article some time after the completion of the Game Jam, so I’ve had a lot of time to reflect. This article has been in the works for some time, but as a new parent I’ve not had much free time. It’s also why I haven’t done any more game jams.

Reception

The reception to the game wasn’t exactly an explosion of interest. In fact, it’s been pretty much nothing, with 5 downloads in 2 years.

So that’s not encouraging. On the plus side, someone played it on YouTube as a Let’s Play! Unfortunately it was Jupiter Hadley playing all the game jam games for a few seconds each, and she didn’t seem impressed with Hotshot Tiler.

So in summary: no-one played it, and those who did got nothing out of it.

Critique

For my first Game Jam (and first shipped game) I’m not going to be too hard on myself. I had a game with a unique gameplay mechanic, decent AI, and pretty bug-free under the conditions. So why did it fail to impress?

I think the main answer here is that it isn’t immediately obvious what the rules, controls or victory conditions are. I think they would become clear with some playful interactions, but no-one is going to invest that much time in a game jam game. Hotshot Tiler is obviously very rough around the edges, so I think coming in and not having any idea what it is would result in most people dismissing it immediately.

Of course, we didn’t even get that far. Five downloads is a pitiful number, but we did literally zero advertising, and had next to no publicity. I think it would be interesting to go back to Hotshot Tiler and see what affect some more work and an advertising campaign could have on that download number.

The game had other issues. It’s not immediately clear that the red background is meant to be a void that will kill your enemies. To be honest, I think it looks too much like a real bug. It’s not until something falls in that it becomes clearly intentional. Similarly, the rules about placing tiles are not clear and I doubt they would become clear playing the game as it is.

I think it’s really cool that I was able to get such good pathfinding into the game, so that you could rearrange the level and the enemies would still try and make their way around the new obstacles. Unfortunately what’s cool to me isn’t really obvious to the player. The Mooks are ruthless and hunt down their prey relentlessly, but much of it happens off-screen. I think they should probably go the sleep to some degree when not on screen, as much of the existing game plays out off-screen. Also the villagers have good pathfinding but awful AI and don’t collide with each other. I feel like some minor tweaks to the villager AI would improve the way the game feels quite a lot.

I think that the time I invested getting the LibGDX pathfinder library working could have been better spent on other things. Something simpler would probably have done they job from the perspective of the player, and I could have invested that time making the rules of the game more clear. I probably wouldn’t have though as it’s only on reflection that it’s become clear to me that was needed; at the time I’d probably have used that time to put the sound effects into the game, or work on the glitch visual effects.

What’s Next

I think I’d like to expand on the concepts of Hotshot Tiler, in particular how the game could be made more fun. I’d like to explore building this as a mobile game as well. I have some loose plans to re-implement Hotshot Tiler in Godot with the things that are missing to see if the game could me made fun. Ultimately though this was a learning experience, and I’m wary of treating it as more than that.