Deathbeam Postmortem
April 21st, 2009 3:52 pmLong post, more after the jump, but the headlines in summary:
- Learning from past mistakes
- Step #1: Add tile mapping
- Step #2: Add collision detection
- Step #3: Add physics
- Step #4: Evolve game concept
- Step #5: Iterate gameplay
- Step #6: Build-out and polish
- Step #7: PAAAANIC!
- Step #8: Deep sigh of relief… followed by panic
Learning from past mistakes
This was my second Ludum Dare, and I learned some lessons the hard way in LD10:
- Coding for games is not the same making a game
- Make sure your toolset includes everything you need to make a game
- Make sure you are comfortable with your tools
- In 48 hours, a simpler idea that you are sure has good gameplay is better than a creative one that might have good gameplay if you do it right
- Art and sound is secondary to gameplay
All of these seem obvious to me now, but I failed on all of them in LD10. The end result of my work was something that vaguely resembled a game, had some gameplay elements, but wasn’t actually a game you could really play.
I spent far too much time trying to come up with a good idea, working on art, and learning Gosu. I didn’t have any sort of map editor and spent most of my time coding tools and base code to do a game. I had a lot of previous experience doing chunks of game code and technical experiments for games, but had never finished a game before, so I got blindsided by a lot of things (no scoring, no win conditions).
So, learning from my mistakes for LD14:
- Didn’t have base code for Pyglet, but I had an idea how to organize things. Many concepts from LD10 for code layout were applicable.
- Knew I was going to use Python, Pyglet, and Tile Studio, and had used them before. Having experience here was a huge plus.
- Came up with a simple idea right away and didn’t deviate from it. It didn’t feel innovative, but I was pretty sure the gameplay would be fun.
- I tried not to make ANY art except when required to test gameplay, and used polygons+color for test art (which ended up being the final art).
My initial concept was to have a tall building filled with personnel. Aliens attack it: their ship is firing a beam down through the building killing people, aliens are busting through windows on jetpacks. You kill one, steal a jetpack and fly around saving people and shooting aliens.
Step #1: Tile mapping (3 hours)
DAY1 NIGHT
I wanted the game to be tile based, so I fired up Tile Studio and wrote an exporter to save directly into Python code. This way I could just import the Python file and write a loader. Loading the tile images and splitting them up was easy with Pyglet.
I didn’t wanted to create 16×16 tiles, but wanted them to be scaled up to 64×64. It took a while to figure out how to set the textures to GL_NEAREST in Pyglet (not hard but it wasn’t immediately clear).
Late night coding killed my progress because my brain wasn’t working. My map was exporting with (0, 0) = top-left, +y down. Pyglet has (0, 0) = bottom-right, +y up. After spinning my wheels for WAY too long, I ended up just reversing the row order during loading and the problem was solved.
Step #2: Add collision detection
DAY2 MORNING/AFTERNOON
Tile Studio allows you to place edges in a map cell to mark collisions. I wanted to support collisions on the top, bottom, left, right, and on bottom-to-top slopes. It didn’t take too long to get the first version working. Unfortunately, the initial collision detection had a lot of bugs.
First problem: you would get pushed out of the tile in the wrong direction (run into the side, popped out the top). I changed it to only collide with edges that opposed their velocity, and that fixed that.
The second, bigger problem was that I was only checking the movement end position, instead of checking the whole line of movement. On certain edge joints, players would would fall through. Bounding volume collision for the movement would take too long, so I had to find another way to fix it.
I struggled for quite a while, but ended up solving it with a hack: I added extra edges on the interior cells people fell into, and ran collision detection twice every frame for the player. The player would still fall through, but the first pass would push him up out of the incorrect tile, and the second pass would push him back out where he was supposed to be.
Although ugly, the solution worked really well — and hey, LD is all about hacks.
The final release had a bunch of collision detection bugs, but that was due to me forgetting to place edges in most of the last third of the map in the rush, not due to the hack.
Step #3: Add physics
Now that I could collide, I started writing some simple physics. I spent WAY too long tweaking this. Originally, you would only use the jetpack bursts, and were to walk a lot (think Halloween Harry). I spent a lot of time tweaking the jumping and walking.
That sucked — because I ended up getting rid of jumping and walking once I added the jetpack. One day I’ll finally learn the lesson about not polishing a product early.
Step #4: Iterate game concept
After adding the jetpack and a simple death beam (that didn’t kill anything, but moved), I started flying around the test map and realized that something was off.
The vertical building for a map was just not going to work, for a couple of reasons:
- The beam’s horizontal movement v.s. your vertical movement didn’t make sense.
- Walking and jumping were boring after getting a jetpack
- There wasn’t enough room to cut loose with the jetpack inside
- It didn’t look like the inside of a building, and I didn’t think I would have time to do enough art to make it look like it
I decided to change the map to be outdoors, and have the user flying left to right, instead of bottom to top. The gameplay could stay mostly the same, except the user would use the jetpack most of the time and would have infinite fuel.
Step #5: Iterate gameplay
DAY2 NIGHT/DAY3 MORNING
I was getting really scared that the concept wouldn’t work since I didn’t have any gameplay yet. I went into overdrive hacking together some temporary tests, and added a civilian pixel that I could grab and pick up and carry around, with the beam chasing me.
The movement felt great, but I didn’t feel enough pressure that beam was right behind me about to kill me. The beam was to your left, but your were constantly moving to the right, and didn’t see the beam. There was nothing slowing you down to keep the beam close to me.
I bounced this problem off of Entar and he threw a ton of ideas at me. Two of them really resonated: tunnels forcing users to backtrack, and turrets. Both were super simple and didn’t much code; tunnels were just part of the map, and turrets just sat in one spot and shot.
I threw both in and it made a HUGE difference. Suddenly it felt like you were fighting a battle. The crappy turret aim ended up hitting the civilians I was carrying instead of me most of the time; but, this ended up being more fun, so I made the player invulnerable to turret fire. You can actually use him to block shots if you’re really good.
I didn’t have a way to actually rescue people, and it still felt like the beam was secondary to dodging the turrets. These two problems solved each other: I added a delay to the rescue platform before it teleported people away. The moment that I saw a pile of pixels get fried by the beam right before they teleported out, I fell in love.
Step #6: Build-out and polish
DAY3 AFTERNOON
Now I had the core gameplay, and spent the rest of my time editing the map and finding ways to make gameplay last longer. It was still too easy to fly to the end of the map, so I had to find ways to force players stay in an area longer.
At one point, while carrying a bunch of humans, I accidentally flew past a tunnel entrance and kept going. I ended up flying a race against the beam to get to the top because I was moving so slowly — BARELY made it over the edge before the beam fried me. I felt myself really getting sucked into the gameplay in that moment.
I ended up changing the area I was working on to force the player to fly up the cliff face, and added civilians nearby that (by that point) people would want to pick up. This ended up making most players share that experience of racing the beam.
I also spent quite a while mashing font code. Rendering fonts in Pyglet was easy, but the 2x scaling I was doing with the rest of the world made the fonts look TERRIBLE when rendered. I had to do some GL trickery to render the fonts unscaled, but allow me to still specify in-game coordinates and have them show up in the right spots.
Step #7: PAAAANIC!
I looked at the clock and realized I had an hour until the deadline. I didn’t have an ending for my map, and there was a massive chunk of unfinished copy pasted tiles that had not been tested. I finished that area as quickly as I could (but not well — which is why the last area is missing so many collision edges).
I hadn’t figured out how a round of gameplay should end, except that the player should escape. Since the jetpack was so prevalent, continuing the trend seemed appropriate. I started drawing a graphic for a big rocket, but a tiny voice in my head was yelling very loudly YOUHAVENOTIMELEFTWHATAREYOUDOINGSTOP. I ended up just making the rocket an even larger pixel.
The biggest heart-stopper was that I didn’t have a way to restart the game on death. I tried a panicked hack of just calling Game.__init__() on my main class again. It yielded spectacularly bad results:
- Accidentally bound the restart to key down instead of key press the first time and inadvertently creating a massive amount of Pyglet windows to the point that my computer hard locked.
- Recreated the player each time it was restarted, and having four players flying around picking up people at once.
Luckily, with slight tweaking it ended up working. Once that was done and I added a win/lose screen, I started prepping for release. Unfortunately, I had never looked at packaging for python, so it took me a bit of beating on py2exe and py2app to figure out how to get the game released.
Step #8: Deep sigh of relief… followed by panic.
After posting my final entry, I ran around testing on computers to make sure it ran. I quickly found that apparently it lagged horribly in spots on every computer except my dev box. Nothing I could do about it, but at least the gameplay was still possible even with the lag.
(After quite a bit of debugging, it ended up that this was due to a combination of smoke particles colliding with tiles and python not being run in optimized -OO mode.)
I also realized that I hadn’t added any instructions. My test graphics ended up in the final, and they were so minimal, people might not know what to do. I threw together some HTML instructions with graphics showing what each thing was.
Once that was done, I walked over to the office to pick up my laptop, and my brother was playing the game with a few co-workers. It was AWESOME to watch people playing a game I had just finished, having fun with it, and competing heatedly with each other… nothing better.
I’ve learned a ton from the experience and it’s renewed my passion for game development… I can’t wait for the next competition!
Tags: postmortem
enjoyed reading this. I love all the similarities of your LD dev cycle to mine… and probably many others.. especially the PAAAANIC! stage..
I had a similar last hour bug that showed up that had me thrashing horribly… some simple line of code in my actionscript had caused all my game area to be black.. and it took many heart stopping minutes to track this down. This being my 5 or 6th 48hr compo I managed to keep my brain quiet enough.. just barely… to get lucky and figure it out.. phew!
I also love reading the moments you ‘fell in love’ with your game / gameplay.. that is a great way of putting it!
I can’t wait to try this game by the way.. I think I’m gonna give it a whirl tonight.
Nice postmortem.