Skip to main content

Invaders: Evolution Post-mortem

Invaders: Evolution is the game I wrote for the 24th Ludum Dare competition. It's the 3rd time I've made a game for LD (as well as one for a MiniLD event) & although I think my games get a bit better each time, I still have so much to learn. This is a post-mortem not just for the game, but for my competition experience as well.


The game is basically Space Invaders, but with a twist: it tells the back-story of the aliens through cut-scenes as you play. It also has two possible endings, one of which is rather unconventional by video game standards. You can try it out, or find out more, here:

What went right

I made a game! There was a low point where I thought I'd have to quit the competition (see the "what went wrong" section), but in the end I came through and got a game finished - and while it's not going to win the competition, it's definitely something I feel proud of.

In particular, Invaders is the first game I've done which tries to tell a story & I think it was successful. Most of the people who've commented on it seem to have got the humour and enjoyed the twist that the story adds.


On the technical side, the code I added to show the dialogue during the cut-scenes actually turned out to be useful for way more than I originally thought. I ended up also using it for spawning aliens, checking victory/loss conditions & transitioning between cut-scenes and play. Just a small amount of code, but so handy! I'll definitely be keeping this around for future comps - in fact, it's general enough that I should be able to reuse it pretty much unchanged.

What went wrong

The first idea I came up with after hearing the theme was a game about the predator/prey relationship between wolves and bison, inspired by this clip from the BBC's Frozen Planet documentary. My head was filled with grand ideas about realistic 3D snow-filled environments, tense chases and even tenser standoffs, smooth lifelike animation... You get the idea. The game I somehow thought I was going to make over the next 48 hours by myself, would probably have been about 6 months work for a small team.

Unfortunately it took me all of the first day and most of the following night to accept this. By the time I finally did, I thought I might have to quit the competition. This was a real emotional low point. When I started to think positively again, and came up with the idea for Invaders: Evolution, I only had about 12 hours left to do it in so it turned into a real rush job.


That meant things had to be cut, just so I could get finished on time. Music was the first thing to go, followed closely by sound effects - any attempt at audio at all, in fact. Shame, because I think some cool retro-sounding space invader noises would have really helped with the atmosphere of the game.

The other thing missing was any real relationship to the theme. Apart from having the word "evolution" in the title. Maybe it's an evolution of the old Space Invaders formula? Pretty tenuous...

Conclusion

The main thing I learnt from this is that I need to spend more time thinking through a game idea to see whether it's actually doable in 48 hours. A bit of time thinking up front can save a whole lot more time later if it prevents you from going up a dead end.

Up 'til now I've avoided doing any kind of time management for my Ludum Dare weekends apart from a vague idea of using day 1 for code, day 2 for assets & polish. I always thought it would make it more like work, less like fun. But with hindsight, the fun actually comes from making something good - and time management helps you to do that. So for the next LD I think I'll try coming up with some estimates and a rough schedule before I start work on an idea. If the schedule won't fit into the 48 hours, well I'll just have to find another idea.

To me, Invaders was a qualified success. I think it could have been a lot better if I'd been able to spend all of the available time on it, instead of just a fraction at the end. I've already greatly improved my basecode as a result of this competition, so my mission for the next LD will be to use my time more effectively. In particular, to make sure I don't have to throw all my work away after the first day.

Thanks for reading!


Comments

Popular posts from this blog

Assert no lock required

This is a technique I learnt about from Jason Gregory's excellent book, Game Engine Architecture (3rd Edition) . If you have a shared resource accessed by multiple threads, where you're fairly certain that it's only ever accessed by one thread at a time, you can use an assert() to check for this at debug time without having to pay the runtime cost of locking a mutex. The implementation is fairly straightforward: class UnnecessaryMutex { public: void lock() { assert(!_locked); _locked = true; } void unlock() { assert(_locked); _locked = false; } private: volatile bool _locked = false; }; #ifdef ENABLE_LOCK_ASSERTS #define BEGIN_ASSERT_LOCK_NOT_REQUIRED(mutex) (mutex).lock() #define END_ASSERT_LOCK_NOT_REQUIRED(mutex) (mutex).unlock() #else #define BEGIN_ASSERT_LOCK_NOT_REQUIRED(mutex) #define END_ASSERT_LOCK_NOT_REQUIRED(mutex) #endif Usage is equally straightforward: UnnecessaryMutex gMutex; void PossiblyOverlappingFunction

Triangle bounding boxes in a single byte

Just thought of a way to store the bounding box for a single triangle in only one byte. It's not really practical or something you'd ever really want to use, but what the hell. Assume we have some kind of indexed mesh structure with a list of vertex positions and a list of triangle indices:   struct Mesh {     std::vector<vec3> verts;     std::vector<uvec3> triangles;   }; We can find the bounding box of a triangle by taking the min and max of all three vertices:   vec3 Mesh::lowerBound(uint32_t tri) const {     vec3 v0 = verts[triangles[tri].x];     vec3 v1 = verts[triangles[tri].y];     vec3 v2 = verts[triangles[tri].z];     return min(min(v0, v1), v2);   }   vec3 Mesh::upperBound(uint32_t tri) const {     vec3 v0 = verts[triangles[tri].x];     vec3 v1 = verts[triangles[tri].y];     vec3 v2 = verts[triangles[tri].z];     return max(max(v0, v1), v2);   } This is nice and simple and probably way better than what I'm about to suggest. W

LD_DEBUG

Posting this mainly as a reminder to myself... If you ever find yourself needing to figure out a dynamic library loading problem on Linux, LD_DEBUG can be a massive help. This is an environment variable you can set to make the dynamic linker print out a ton of useful diagnostic info. There are a number of different values which control the amount and type of diagnostics printed. One of the values is help; if you set LD_DEBUG to this and run executable it will print out a list of all the available options along with brief descriptions. For example, on my Linux workstation at the office: > LD_DEBUG=help cat Valid options for the LD_DEBUG environment variable are: libs display library search paths reloc display relocation processing files display progress for input file symbols display symbol table processing bindings display information about symbol binding versions display version dependencies all all previous options combi