ChaoticEngine

LudumDare 10 Postmortem

Introduction

Tuesday, 18 December 2007

This past weekend I participated in LudumDare 10, a 48-hour game development competition. Although I failed to complete my entry, I still consider it a successful competition - the reason: I learned a lot. Some of the things learnt include:

  • How to approach a competition with such a short deadline.
  • How to make a tile based map work without making it too messy.
  • That CEngine is a terrible excuse for a library - it needs a lot of work before it will be useful to anybody.
  • How to create some nice animation effects with GIMP.

 

How to approach these competitions

The type of game I tried to make could probably be classified as a crossing between puzzle and RTS. I spent some time looking at the some of the successful entries made by the more experienced competitors. The list of entries I looked at are:

 

The big thing that struck me about all of these entries is that they were all either arcade games or puzzle games. The thing that ruined my entry was that I spent 9 hours trying to get path-finding working - these games didn't need path-finding! So I think choosing what type of game you're making carefully is essential - simple genres like arcade or puzzle should be preferred over more complex ones like FPS or RTS.

 

Tile-based maps

This was not the first time I tried to make a game with a multi-layered tile based map, but it was the first time I really got it working well. The biggest problem I usually run into is answering this question: who is responsible for moving units - the map or the unit?

If the map is responsible, it needs to understand some of the logic of where a unit can and cannot move, so you have to import your unit classes in your map code. But ideally, when the player wants to move a unit you simply want to do unit.moveTo(coordinate), so your unit then needs to be able to do map.moveUnitTo(self, coordinate) - congratulations, you have circular dependency. Now create some convoluted scheme that uses pygame events for communication between maps and units that turns out a mess.

 

If the unit is responsible, the map simply needs to know that there is a unit at a given location (doesn't need details so you don't have to import your unit definitions). So now you can do something like unit.moveTo(map, coordinate) - the unit uses the map to determine if this is a valid move, updates it's coordinates and tells the map to move it to the new position. The only downside is that you have to pass the map to your moveTo method and your unit needs to store it's current coordinate so it can tell the map to vacate it's old position.

 

Before the competition the second option was was a vague idea that I hadn't tried yet - during the competition I not only got it working, but even improved it by making Map a singleton, so now units could do Map.getInstance() instead of having to pass the map whenever you call method that has the unit interacting with the map.

 

I think a multi-layered, tile-based map is a common enough concept that I should probably include some classes for doing that in a generic way in CEngine. A useful spin-off of that would be to add A* path-finding where the user of the library simply needs to define how map tiles are connected and how to compare nodes in the graph.

 

CEngine problems

I discovered several problems with CEngine during and shortly after the competition:

 

Poor sprite rendering performance

The sprite renderer couldn't manage more than about 500 sprites, without dropping to less than 10 FPS.

Upon further investigation it looks like even with optimizations like putting all frames of a sprite on a single texture and making display lists of sprite quads I still can't get the level of performance I want. Perhaps Python just isn't suited to pushing lots of OpenGL commands through quickly.


Sprite configuration is painful

Configuring your sprites is painful - especially once you run into sprites with 10 or more frames and multiple states. This  becomes very noticeable once you try to create a large volume of sprites in a short time.


Sprites lack features

There are several features that I wished I had during the competition:

  • Sprite rotation & translation: Can't define sprite rotation / translation as part of the animation. Would have been useful for making a glob bounce while moving and moving it to an adjacent tile as part of the animation. Rolling boulder sprite could have been done with 1 image + rotation, instead of N pre-rotated images.
  • Better sprite state management: Instead of simply telling the sprite to go into a state, it would be nice to also be able to tell it to go into a state, play the animation N times and then automatically go into another state. For example, upon summoning a glob it will be in a summoning state (showing a puff of smoke), once the animation is done it will automatically go into its idle state.

 

ATI problems

Textures that don't have power of 2 sizes cause texture corruption and terrible performance on the ATI 9600 in my laptop. This was fixed in CEngine 0.1.1.


No Windows port

This was implemented in GlobCombat 0.1.1, which I released some time after the competition.

 

GIMP tricks

The coolest GIMP trick I figured out during the competition is the one I used to create the air rune with the clouds flying through it. Here are the steps I followed:

  1. Create a new image, of the size you want the sprite to be, with a transparent background.
  2. Now use one of the fonts to print the character.
  3. Use the magic wand select tool to select the letter.
  4. From the edit menu, select clear (or press Delete) to restore the contents of your selection to transparent - you retain the shape of your selection though.
  5. Create a new image that is wider than the target image.
  6. In this new image use the clone tool or pattern fill to fill it with some repeating pattern (like clouds).
  7. Select the whole image and copy it (Ctrl-a Ctrl-c).
  8. Now select the first image again and in the edit menu select paste into - the tiled image is pasted into your selection.
  9. Use the move tool to move your pasted layer around - your selection basically acts as a window onto this layer.
  10. Save your image.
  11. Repeat steps 9-10 as needed.

 

For the rest I mostly used patterns and the various filters, I just experimented as I needed something to find a way of doing it.