My legs. They are wibbly wobbly.
Ahh, Saturday. 5PM PDT. The end of PyWeek. That moment when I stand up on my now wibbly-wobbly hind legs and let these worn down nubs that used to be fingers grow back. It's been a rough week but in different ways than previous PyWeeks. This was quite different than my previous experiences. It probably wasn't my best PyWeek either, and as such I've learned quite a few things.
On a happy note, though. I'm not dizzy and hallucinating the way I was directly after PyWeeks 10 and 11.
Typically our team structure is as follows...
- 1 or 2 people coding
- 1 person on pixel art (sprites-n-tiles)
- 1 person on large art (backgrounds, portraits, and cutscene art)
- 1 person on music
- n people on assets where n is proportional to the size of the game we ultimately plan to make.
If n is large enough, I will lay down basic architecture and infrastructure in the first hour of PyWeek to unblock the other coder and then spend the rest of the night (probably to sunrise) making a fancy dancy UI map editor for the n asset-development people. For PyWeek 10 we made a story-based platform game. For PyWeek 11 we made a overhead-2D zelda-style dungeon adventure game. Therefore n was assigned a large value.
This time we were making a real time strategy game and the intent was to have auto-generated levels for custom games and 9 levels for the story-mode game.
"Gee, 9 doesn't sound like a big number for total number of levels"
So I made the mistake of assigning n the value of 1. In Shattered Silence (yup, that's the game's name) you start out with a very small map. Once you complete that map, the bounds of the map increase by a factor of 3 on each edge (so the area is "9 times" the area it was before). There are 9 zoom levels, so you have to beat the level 9 times.
The side length of the 9th level would equal three to the power of eight times the original map width. (power of 8 because there are 8 zoom incrementations for 9 levels)
That's a big number for a side length. If it was strictly following this pattern, the final level would be 6561 times larger on each side taking up a total area 43,046,721 times larger than the original level. Because no one here owns a computer with 22 gigs of RAM, we tweaked it a few times such that the actual zoom is sneakily a bit smaller and midway (between levels 5 and 6) we reset gameplay to an abstracted view where you no longer control individuals, but icons denoting crowds of people instead. Looking back, it reminds me of Katamari Damacy's zooming system. This made it manageable and still runs 120 fps without making my laptop burst into flames. But still, it was quite a bit more to map out than I had anticipated.
Mistake 2: leaving map making till the 2nd half of the week. Had I done this earlier, I would have realized that these maps are pretty huge. Second, the person that was going to do the maps had a personal emergency come up on Thursday. Three of the teammates were unavailble post-Thursday and had their work finished up. The other 2 had full work item queues. So unfortunately quite a few things had to give to get the maps to a playable level.
Lessons:
- Seemingly innocent tasks can balloon really quickly
- As much as any of us can dig ourselves into a bunker and live off a feeding tube with no scheduled distractions for the whole week, life has a funny way of catching us by surprise. Recovering from such surprises is much easier to do when work is accurately estimated before Wednesday.
The other lesson Ive learned is that the game idea has to be something /everyone/ on the team feels passionately about. We felt okay about the idea we had for 9 times when we chose it, but only okay. Once we started to sit down and create it, no one was nearly as excited as they were in previous PyWeeks. There was probably a few reasons for this. One reason could have possibly been that there was no fully functional game until late in the week. The whole engine was comprised of numerous small moving parts and couldn't really be enjoyed until at least most of them were completed.
Previously, we made games where you run around and do things. The "run around" bit took about 2 days and "do things" bits took the other 5. But at the very minimum you could see concrete progress and that probably helped morale tremendously without us realizing it until we saw the lack of that this time.
Lesson:
- When morale starts to dip, it's really contagious.
- Make an ugly game that works in 1 or 2 days. Make it work nicely in 2 more days. Spend the final 3 days on polish. If everyone comes down with the plague on Wednesday, then hey, you still have a game that "works nicely".
So those are the lessons I've learned. I've often claimed that "PyWeek is the time to perform, not the time to learn" but this time I've learned that learning is unavoidable. Anyways, it's time for me to return back into hibernation in the real world for another ~5 months until PyWeek 13 when I'm sure I'll learn several new and different lessons. Hopefully my fingers will have grown back by then.
It's been a rough week and even though there were several factors working against us, I am still pretty pleased with the final outcome. Once again, we have really awesome art thanks to Spears and first-timer Niels, and we have much more sophisticated (in a good way) menus than we ever have before thanks to Falun's efforts.
I hope you enjoy it. :)
...
Oh, and a Completely Random lesson:
PyGame's Surface.set_alpha() method does not work on surfaces that contain per-pixel transparency. However, it can be cleverly hacked around. Suppose you wanted to accomplish something that looks like this:
# surfA is a 100x100 surface that contains transparency
# screen is the game screen that you want to blit it to
surfA.set_alpha(128) # will not work because PyGame is silly. SILLY!
screen.blit(surfA, (x, y))
Instead, try this:
tempSurf = pygame.Surface((surfA.get_width(), surfB.get_width())
tempSurf.blit(screen, (-x, -y))
tempSurf.blit(surfA, (0, 0))
tempSurf.set_alpha(128) # this WILL work
screen.blit(tempSurf, (x, y))
(log in to comment)
Comments
If you want a generalized version of our transparency hackery I ended up taking the same approach as Blake except dropped in some nifty portable functions:
def _dupe(surf):
return pygame.Surface((surf.get_width(), surf.get_height()))
def _drawon(target, text, loc, op):
s = _dupe(text)
s.set_alpha(op)
s.blit(target.subsurface(pygame.Rect(loc[0], loc[1], text.get_width(), text.get_height())), (0,0))
s.blit(text, (0,0))
target.blit(s, loc)
richard on 2011/04/10 06:51:
Great write-up, thanks! I seem to recall learning some similar lessons the last time I participated in a large team. I should try it again some time though.