My Ludum Dare experience
Last weekend, I took part in Ludum Dare for the first time. Ludum Dare is a game development competition in which you have to make a game in 48 hours. Before the competition, everybody votes on a theme, and this time around the most popular theme was “Connected Worlds.” By the end, I had successfully managed to make a little game called Connected Worlds Training Facility which you can play online.
Taking part had been on my mind for some years. I first learned to code with Game Maker, about 12 years ago, and have been interested in game development ever since. I read a lot of game development books, regularly browsed forums like /r/gamedev and the Stack Exchange site, learned a fair few libraries and tools, and started making some of the ideas that were dancing in the back of my head — but never got far.
I watched many Ludum Dares come and go and always told myself that I'd take part next time. I was also pretty scared that once I left university and started working, I'd never have the time for that kind of thing. Then I left university, got myself a job, and three weeks later took part in Ludum Dare. It's funny how that happens.
I only found out it was happening a couple of days before it started, so I didn't have much time to prepare. I quickly decided that I was going to do it in Java, although C++ is by far the language I know best. I just knew I would be faster with Java, particularly thanks to IntelliJ IDEA being a pretty great IDE. I've written plenty of Java in the past, most recently for Android applications, so I'm comfortable writing it without having to think about the syntax. I had also been meaning to try out libGDX, a cross-platform Java game framework, for a while.
One thing's for certain: 48 hours is not a very long time, so I immediately planned out in my head how I wanted the weekend to go. I was thinking about one day for the core mechanics, then a second day for adding content and improving the graphics and audio.
The night before it began, I spent 3 hours or so skimming through the libGDX Wiki so I had a general idea of what features were available and how I might use them. I knew I wasn't very prepared, but I was going to do it anyway.
How it happened
First thing on Saturday, I found out the theme — Connected Worlds — and started thinking about ideas. I had previously had ideas about a co-op game involving two players in two separate environments that could interact with each other. This became the basis for my Ludum Dare game - the core mechanic would involve switching between two worlds and using connections between those worlds to solve puzzles.
I deviated from my plan immediately, of course, and decided I'd start with pixelated rendering. Why not? I knew how to do this in OpenGL; it was just a matter of working out how to do it with the libGDX API. An hour after getting started, I was rendering my game to a quarter-size Renderbuffer Object and then rendering that Renderbuffer Object to the full-size Default Framebuffer.
I then got to work on the platforming mechanics. This meant getting some simple sprites drawn to the screen, modelling the character and environment in Box2D (which I had also never used before), and getting the character to respond to input. Two hours later, I had a jumping man:
The green and pink boxes are very helpfully rendered by libGDX's debug renderer for Box2D. With a couple of lines of code, it renders the outlines of all your physical bodies.
Half an hour later, I had a bit more movement, but the character was able to jump infinitely and slide around like an ice skater:
Then came the biggest time waste of the entire weekend; I spent 5 hours trying to get the movement feeling natural. The good news is that it worked. The bad news is that I lost 5 hours. Unfortunately, it took so long because I was trying to figure out Box2D at the same time. It turns out there is an internal limit placed on the velocity of objects which I was hitting quickly because I was using pixels as my units. The recommendation is to scale everything to something like “meters”, which I really should have listened to in the first place. Lesson learned. I finally had movement I liked (with variable jump height!):
I then decided to make a sprite for my character. The first idea that came to my head was a little dude in a massive hazmat suit, so I just stuck with it. This is what I drew up in Gimp:
Next, I animated the sprite (4 frames of animation), rendered it, and added smooth camera movement. Smooth cameras are important to me, okay!
I realized then that I still didn't have any actual environment. I decided to keep the physical model entirely separate from the actual environment; I would just ensure that the obstacles match up with where the walls and objects in the environment are supposed to be. To render the environment, I used libGDX's fantastic tile map functionality. I created a simple tile map (containing only blue circles) with Tiled and, with 4 lines of code, rendered it to the screen:
This was the end of the first day. My core mechanic — switching between worlds — was nowhere to be seen. At least I had nice player and camera movement, right?
The next morning, I made a more realistic environment and added a few more tiles to the tile map:
I obviously needed to get on with the core mechanic, so I added buttons which would provide the interaction between one world and the other. As usual, I spent a bit of time trying to improve the feel of the buttons. There's a Box2D body that moves down when you stand on it. This body then springs up after a short delay.
Of course, what is a platformer without moving platforms and doors? The plan was to connect the buttons in one world to the doors in the other. I also had ideas of buttons that would make platforms start and stop moving but never got round to that.
Six hours into the second day, I went back into time-waste mode and decided to add some post-processing graphical effects. The intention was that the world switch would look like a change between two CCTV feeds, so I wanted to make the game look like it was being recorded:
I then finally added the world-switching mechanic! To do this, I had to break apart quite a bit of my code. I extracted all of the per-world content (which is pretty much everything you see) into a class that is responsible for generating the level. This level class is given two world data containers, which it populates with the per-world content. At the time, I just had both worlds exactly the same except for the player objects:
If you notice, the moving platform starts back at the bottom when the world is switched. I didn't like the idea that one world was paused while the other was running, so I changed it so that both worlds' physics were being updated at all times.
To make the second player sprite, I cheated a little and just colourized the hazmat suit in Gimp. For the static transition effect, I have a sprite map with 6 noise sprites within it, and I switch between them randomly, tiling each one across the entire screen for a short duration.
I then went into overdrive mode. I had 8 hours left of the competition and had only just gotten the core mechanic working. For that reason, I stopped making videos of my progress, but here's a quick summary of what happened:
- I created the first level of the game — a training level that teaches the controls and the world-switching mechanic.
- I improved the sprite set, adding tiles for various kinds of wall connections, better sprites for the moving platforms, and decorative tiles for the background just to make it look a little more lively.
- I added a goal to the levels — a special golden button that both players must stand on to reach the next level.
- I added transitions between levels. This also involved breaking my code apart a bit, to figure out which things should be loaded only once at the beginning of the game and which things should be loaded at the beginning of each level.
- I quickly generated some sounds with as3sfxr and threw them in. I wasn't exactly pleased with this because I had wanted to make more atmospheric sound effects, but I just didn't have time. I think silence would have been worse.
- In the last hour, I put together a second level. I had been thinking about making 5 levels in total, but there was clearly nowhere near enough time. My level creation process was very slow because I had to manually enter the coordinates of every physical body and object. Ideally, I would have a unified level file that would provide this information together with the environment tile map.
- The finishing touch was to add a couple of pseudo-levels that just wrote some intro story text and said “To be continued” at the end.
The final sprite sheets looked like this:
And the final submission of the game looked like this:
Quite a lot different from before, wouldn't you say? I was pretty happy with the result, but disappointed that I didn't have chance to add more levels and better sound effects and music. I had been completely unable to stick to my planned timeline; who'd have thought it?
Taking part in Ludum Dare taught me a lot about myself and how to make games quickly:
- Being a confident programmer is very important. The more practice you get, the more confident you'll be. The more confident you are, the more you can focus on making your game.
- Clean code is not a priority when trying to work quickly. The code I wrote is hideous, and I would normally feel sick about it, but for this I don't care.
- Use existing libraries whenever possible. I tend to find problems with many libraries I use (libGDX is no exception), but they get the job done. There's not enough time to do everything from scratch.
- A good IDE makes a big difference. I can't imagine the pace I'd have been going at if I'd been using vim and bash like usual. IntelliJ IDEA was a huge boon.
- The little details matter. The details can do a huge amount to make a creative work look and feel better. There are still a few tiny issues with the game that bug me, however.
- The little details waste time. On the other hand, my attention to detail wasted a lot of time. A good balance is important.
- Prepare and practise beforehand. I could probably have regained a large chunk of the first day if I had practised making platformers with libGDX in advance.
- 48 hours is a short time. Work quickly, focus, and don't expect to make anything incredible. Simple, bite-size ideas are much better.
- 48 hours is a long time. A hell of a lot of work can be done in 48 hours. Even one hour is enough to do something productive. Got some time? Make something!
It's safe to say I had a lot of fun taking part in Ludum Dare. It required a lot of effort, but it was worth it and didn't really feel like the huge undertaking that I was expecting it to be. I was surprisingly relaxed throughout the whole thing. It's been really great to see all the helpful comments from people who have played my game and it's been fun to give feedback on other people's games too. I look forward to the results.
If you're like me and have been meaning to enter Ludum Dare for a long time, just think about how worthwhile it'll be. You'll enjoy it, no doubt. I hope to do it again sometime soon.