I'd like everyone to post an entry postmortem, even if (like me) you failed to produce a complete game. Just present what you do have and perhaps touch on the following questions:
1. what did you learn? 2. what went right? 3. what went wrong? 4. what's lispy about my entry? 5. what interesting algorithms or designs did i use?
Thanks everyone for participating!
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
A postmortem, huh?
What I have is about 970 lines of code over nine files, imaginatively named "game-stuff". There is possibly the beginnings of a classic dungeon crawl (such as Wizardry or Shining in the Darkness) in there, but it would need at least another few days of work to bring it out.
1. What I learned:
* The heart of a UI toolkit is event and redraw management, and is easier to build than I had thought.
* Not being able to create game art is a serious handicap for a game developer, and leads to using more "abstract" visuals ("The pentagon hath smote thy shoulder for five damage."). This is fine as a stylistic choice, to a point, but it is far better to not be virtually forced into it.
* Not running your game loop in its own thread adversely impacts SLIME behavior.
* Running your game loop in its own thread makes use of *standard-output* for information gathering more annoying.
* Model-view-controller, or at least view-controller, is still king.
* Text rendering is still annoying at best.
* For short-timescale development, such as a 7-day compo, it helps to have an idea of what you're going to do when you start, actual practice using the libraries and tools before you start, and to be able to leverage existing code. I expect that the next time I try one of these events I'll be better prepared, simply from having a better idea of what I'm doing, from the code that I produced this time already written, and possibly from having improved the code in the interim.
* As a unit-test library SB-RT is lame. It might be a tolerable starting point, but it desperately needs some utilities layered over the top.
2. What went right:
* Small files and one package per file. This has a number of effects, mostly promoting the definition of small, close-to-explicitly defined protocols between parts of the system. Small files applies a certain "pressure" towards each file doing just one thing. One package per file leads to explicitly defining what that one thing is and how to make it happen. On the whole, it's API and protocol design in the small. I'm not sure how well this scales, but I'm interested in finding out.
* Being able to discuss problems on IRC. Especially if it's a problem with an unfamiliar library (such as OpenGL), or a problem with common tools such as SLIME, or how to structure some part of your system, feedback and suggestions from other people can help immensely.
* Using OpenGL for 2d rendering. I don't know if this went right, or simply didn't go wrong, but I'll give it the benefit of the doubt.
* Swiping ideas from other people and code from my own copious archives (though there's not much -game- code in my archives).
3. What went wrong:
* The GLX implementation I used is still more than a little rough around the edges (incomplete, not well documented, and has an awful tendency to ignore commands if the parameters are of the "wrong" type).
* I was badly distracted in several different directions over the course of the event, including attempting to get a video capture device working properly in one of my computers.
* The tool support for working with lots of small packages instead of few large packages is either non-obvious or non-existent. This was most noticeable when "splitting" a package, moving some of its functionality to a new package.
* I have no mouth texture image, and I must scream.
4. What was lispy:
* I used SBCL, CLX, and the GLX implementation in CLX.
* Redefining input handling and redraw completely while the main loop was still running.
* Using generic functions for output redraw control.
* Using first-class functions for input handling.
5. What interesting algorithms or designs were involved:
* I swiped the pseudo-3d maze bits from an old X program I wrote back in 2004 that used line-drawing primitives to lay out the maze views (much as the original Apple ][ Wizardry did).
* In order to issue a glCallLists request for drawing text on the screen, I copy the string to be output and smash up the type and length bits of the copy to be consistent with an octet vector, then ship the octet vector off to the server. This is horribly SBCL-specific, but it gets the job done.
* All visual output ended up being performed in terms of "widget" objects that were on a global list. This list was set up via defparameter, so I ended up being able to experiment with new widgets and widget parameters by simply reevaluating a variable initialization instead of involving the main redraw function or reinitializing the entire main loop.
6. What I believe I need to do going forward.
* Start working on producing decent 2d game art. This is one of my major handicaps, and I'd like it to be at least partly compensated for.
* The input model in game-stuff is lame, and needs a rewrite. Just as soon as I figure out what wouldn't be lame.
* My current text-message widget is static-text only. Being able to treat it as an output stream, or at least to be able to write new messages into it without recompiling it, would make it actually -useful-.
* Fix some of the outstanding issues with the GLX I'm using.
* Learn more about the OpenGL rendering model, especially the matrix stack and various glEnable options.
* Produce an actual, end-to-end, full game. Even if the plot is basically "go down to the bottom of the dungeon, kill the evil wizard Riatsala and recover his Book of Primary Aspiration" it would at least be a full game, and that would be a first for me.
* Sink some effort into improving my tools, both in terms of development environment and in terms of available libraries and documentation. Note that this expressly does -not- mean compiler-backend hacks (my usual playground).
I think that's basically it, and I'm looking forward to the next LGDC.
-- Alastair Bridgewater