On Mar 29, 2010, at 1:23 PM, "David O'Toole" dto1138@gmail.com wrote:
I'd like everyone to post an entry postmortem
My game is called Roto Mortar [1]. When I started trying to come up with a game that I might be able to pull off in a week, I remembered an exercise from the book: Game Design Workshop [2]. The exercise was to design a game with a one-button interface.
The concept I came up with for this contest was that you were trying to defend a base against attack. You've got two mortar cannons. Unfortunately, their controls have been blasted. Lucky for you, Ace Repair Guy Billy Bob has rigged one of them to rotate and hooked up a button that let's you tilt it while you hold the button and fires when you release it.
Here is the game announcement I made on my blog [3].
What I got done was the basic shell of the game. There is a quick title screen with a message from Billy Bob setting the stage. Then, you're plopped into the game. Another message from Billy Bob announces that he's gotten one turret back online.
The game scenery, enemies, and projectiles are all UV textured, X3D objects made in Blender with PNG textures. The game uses shadow- volume techniques to overlay a range and angle indicator onto the scenery so you can tell where you're aiming. Those shadow volumes are non-textured, X3D objects built in blender.
I used my own code for font-rendering in cl-OpenGL using zpb-ttf [4] to render the messages from Billy Bob and the opening title. I used cl-png to load the PNG files (textures and Billy Bob icon). I used a custom parser atop CXML SAX parsing to read the X3D files. I used CLOS throughout and made much use of the PROGN method combination.
[1] http://nklein.com/software/roto-mortar/
[2] http://www.amazon.com/Game-Design-Workshop-Prototyping-Playtesting/dp/157820...
[3] http://tinyurl.com/ykeahl8
[4] http://nklein.com/2010/01/better-text-rendering-with-cl-opengl-and-zpb-ttf/
- what did you learn?
I had read about Shadow Volume rendering in OpenGL several times before. This is the first that I actually did it. I could probably do it again now without references.
I had a bunch of my methods return a new 'screen instance if you want to switch screens. I think, if I were to do it again, I'd either make a robust check on the receiving end to ignore return results that aren't of type screen or add a slot to the screen class that is NIL until it's time to switch screens. As it was, I accidentally returned T or a mesh or something incompatible from my event handlers way too many times (especially by the end of the second all-nighter).
- what went right?
I drew a simple UV cube in Blender. I exported it to every choice on the export menu. After looking a bit, I opted for X3D. It's badly structured XML, but not too hard to parse. (I say badly structured because its lists of number are all encoded into strings. Rather than stuff like <coordinates><coord><x>3.5</x><y>2.0</y></coord>...</ coordinates> or even: <coordinates><coord>3.5 2.0</coord>...</ coordinates> they instead have attributes of comma-separated lists coordinates="3.5 2.0, ...".)
Anyhow, I think X3D was a good choice... easy to get going. And, with baked textures from Blender, things looked pretty good.
I was glad I had already published the font-rendering code cuz I could otherwise have a day there. As it was, I just pulled in the font.lisp I had published earlier [4].
I used SBCL's save-lisp-and-die to make a command-line executable and Platypus to wrap that up as an OS X executable. Those both worked great.
- what went wrong?
As I mentioned, having each event handler for a screen possibly return a new screen seems like a cool, lispy idea. Really, it is easy to mess up when you're tired.
I spent lots of time, too, trying to figure out which attributes I hadn't saved that I should have in one place which screwed up things in another place.
I also spent way too much time working around the OpenGL matrix methodology. What I needed to do for my direction-indicator and range- indicator was... translate to where they need to be and draw them at the appropriate angle and scale. What I wanted to be able to do is set up the angle and scale and then translate things and draw. If OpenGL had more room available on the :projection matrix stack or another :model-local matrix after the :model-view, I could have reused code much more. As it was, I had to make some custom draw code for these objects to get the rotation and scaling to happen after the translation.
There is a whole list at [1] of things I wanted to get done in a week, but didn't.
There may well be an option to CXML to get it to ignore the DOCTYPE declaration. But, I just manually removed them from the X3D files to keep CXML from trying to fetch them.
- what's lispy about my entry?
In the game-play itself? Nothing.
In the implementation, I used PROGN method combination to great effect here. In particular:
(defmethod update-item (item delta-time) (:method-combination progn :most-specific-last))
This way, the base object can track total time elapsed since some point, but derived objects can use either that total time or the most recent delta or an intermediate classes total time since some other indicator.
The XML parsing also makes use of interning symbols to avoid lots of string compares.
- what interesting algorithms or designs did i use?
The basis of the X3D parser was generated from other Lisp code that I was working on a few weeks back.
And, the PROGN thing was pretty cool.
And, the shadow volume technique is pretty flexible for projecting stuff onto surfaces.
-- Patrick pat@nklein.com