PyWeek 4 post-mortem
Another year, another PyWeek. In case you missed it, my entry was
. Here's my thoughts on how it went,
what I learned, what I'll do next time. Sorry for the longevity.
For interest, my previous two PyWeek entries were
[with another programmer and an artist] and Nelly's Rooftop
Like last year, I started with no design, or even ideas (for the theme that
was voted in). I had committed myself to these goals though:
* Making a 3D game instead of the usual 2D fare. Incidentally, this removed
most of the advantage of using pyglet (since getting a GL context in
pygame is not particularly different).
* Creating a look that isn't cartoony, and gameplay that isn't
These are commonly seen on pyweek entries (both of my previous ones), and
in indie games in general. I think this look is a bit of a cop-out,
because it's relatively easy to draw cartoon characters, have them move in
silly ways and choose colours that don't go together. Of course, not
being an artist I was basically dooming myself to disappointment here.
* Making the game *hard*. Because I'm not much of a gamer, I find this
tricky to judge. My previous efforts turned out to be far too easy for
anyone who plays games even semi-regularly.
Going 3D but lacking any character animation tools or library meant it had to
be first-person. And because I lack imagination, that meant it had to be an
I didn't figure out the game design until day 2:
An orbital city has fallen into the clouds. It's your job to boost it
back into orbit by activating various systems around the city. After each
boost, the city is further from Earth, and so has less gravity, allowing
you to reach more areas (this is both the theme element and primary
novelty of the game). There are some extradimensional creatures which
seek to hamper your efforts.
The "city" very quickly was revised to be an orbiting space station or
satellite, after thinking about how good a modeller I am not.
I had some very specific influences in mind during the week:
* ICO and Shadow of the Collossus. Both
games have largely untold back-stories (nowhere in delta-v is the story
revealed). The baddies are based on the small creatures from ICO:
small, shadowy, difficult to focus on -- I think they're really scary, and
tried to make my ones similar. The lack of a HUD (besides the crosshair,
without which shooting with any accuracy is impossible): the use of
motion-blur in place of a health meter was very deliberate, as was the
placement of the weapon charge indicator on the weapon itself.
* Unreal Tournament 2004 -- the game of choice in the graphics lab at uni.
I've only recently started playing, and am quite the amateur compared to
the guys that play regularly. I tried to recreate the feel and dynamics,
especially of the low-gravity maps. The weapon is also a based on the
You can read sketchy in-progress diary entries on the PyWeek site. The main
point was that I tried to get gameplay in early. In both *Brains* and *Nelly*
this was added only in the final hours. This didn't leave time for tweaking
or experimenting, or even deciding whether or not the idea would work.
I have a friend whose day job is writing mobile phone games, and he explained
to me sometime last year how gameplay is implemented even before graphics: an
action-oriented boxing game began life as basically a text adventure.
While I didn't take things to that extreme (among other reasons, I hadn't
completely figured out the gameplay when I started), I did have the majority
of the triggers for winning/losing implemented by day three. Getting the
complete game together though needed the whole level (station) to be designed
and modelled first. It was finished on day 6, which is a good 24 hours better
than last year, so I'm pleased with that, though that's an obivous thing to
tackle more next time.
I did unfortunately get bogged down rewriting existing, working code (the OBJ
loader). This is a habit I'm breaking very slowly; I knew I was doing bad
while I was doing it, but that was nearly balanced by some pretty good
justifications (the existing loader had no support for named objects within
the file, for drawing meshes efficiently outside of a display list, or for
accessing the polygons and materials programmatically). [Incidentally, I was
reading the latest on COLLADA last week and I'm excited: we need to base
pyglet's 3D object model around it.]
**How it works**
For lack of a real level editor, I used Wings 3D to model the entire station
in one file. The indoor sections are separate objects, allowing me to toggle
the walls into locked/wireframe for easier editing. The player's starting
position, baddie spawn points, particle emitter sources are all just cubes
placed in the appropriate place. The triggers that the player walks over are
two-sided faces co-planar with the floor but raised slightly above it.
When the level is loaded, objects ("meshes" in delta-v source) are
by name from the loaded OBJ file. They are compiled into display lists
explicitly (objects that change material obviously cannot be compiled).
When not compiled, meshes are drawn one material at a time with interleaved
vertex arrays. This is not noticeably slower (by FPS counter) than using a
display list (because Python is so freakin' slow compared to the graphics
euclid is used for vector math (I tried some matrix math too, but it didn't
work... maybe a euclid bug, maybe not). I discovered some bugs in euclid that
can be chalked up to it never being used before. More importantly, I
discovered that its geometry classes are really useless. Who needs to find
the closest distance between two abstract geometries? Why is there no method
to determine the contact point(s) of a collision? Why can't transforms be
applied to the geometries? I'm planning a big rethink of all this, including
making it easier to add more geometries (delta-v includes rough Ellipsoid
and ConvexPolygon classes, neither of which can be easily integrated into
euclid without hacking the library). If euclid had included the functionality
it should have, I probably would've saved two days of development (I'm not too
quick with the maths).
The player and baddies are represented by an ellipsoid for finding body
collisions, and a line segment where you'd normally find your shin (for
finding floor collisions). The environment is a set of ConvexPolygons, which
can be approximately tested for collision with the ellipsoids (Google for the
"bevelling" problem of why it's not exact). Player/baddie collisions
modelled as ellipsoid/ellipsoid. There's some bug here which you can see if
you turn on collision visualisation (in fps.py), but it's not noticeable in
Originally every ellipsoid was checked against every polygon every frame.
This worked fine until I added more than two or three baddies, which killed
performance. My quick-n-hacky solution was to bin the polygons into a sparse
uniform grid (a dict with (x,y,z) coordinate keys). I think this is a really
good Pythonic solution: dicts are fast and easy. In C, dicts would be very
tricky, and I'd probably use a BSP tree or octree, both of which would be
tricky and slow (or complicated to avoid the function recursion) in Python.
The game has around 22,000 grid cells populated with an average of 3 polygons
each. Grid cells are more than twice the radius of the player or baddie, so
checks against more than one cell are relatively uncommon. (Note that the
grid is static with the geometry: the player or baddies are not inserted into
I'm waiting on feedback from gamers, to see if I got the FPS dynamics right,
and how hard the game is. The game is easy to mod: all the gameplay is
within "level.py" and the OBJ files it specifies. Could delta-v
Python equivalent of the Unreal engine? Probably not.. but hey, look! An FPS
[Richard just pinged me: "dude, your game is teh cool, but haaard"]