How our Pyweek23 went - codogupyweek23The team name is a mashup of our IRC nicks.
DR0ID was the beast of burden upon whose back we rode. His tools (pyknic, pytmxloader), skills and knowledge with Python and Tiled, carried the team. Kudos to DR0ID.
Axiom and Gummbum were challenged to give as much time to Pyweek. Life's demands. Yet we were able to contribute in significant ways.
Gummbum lost two-plus days to life's demands and could not do much until Tuesday. DR0ID lost a couple days to collision-detection and -handling. Some days went by with many hours of no submitted changes. So essentially what you see here is probably a 5-day game at most.
Axiom contributed early by locating some great art and music. He kept us sharp during the week by playtesting. If you want to find the worst bugs, he is your man. :D All true, funny, and invaluable.
So, by Sunday (Day 1) we had the game design well documented. There were far too many features to implement, and the simplistic core mechanics would be a difficult challenge.
Gumm's vision was a Vanguard-like (a 1981 arcade shooter on rails: https://www.youtube.com/watch?v=4MwVWsHBm5g), but we didn't want a clone. Just to get some early controls and interaction, DR0ID implemented the player ship as a free-roaming entity. Partway into the week it was deemed that putting the game in "runaway train" mode (a la Vanguard) would create some difficult problems that we didn't have time for. So the free-roaming control was kept. But this changed the game's focus. Instead of keeping up with the runway train, the player now set his own pace.
We had a tiny test map that was doing the job. Gumm made a giant map on Wednesday. Wonderful. When DR0ID added rotation to the meteors, it killed performance, of course. Image caching to the rescue! But then we met with an Out Of Memory (OOM) error, hitting the 32-bit limit (3 GB!?)
Well, sooner or later you have to make some real-time monitors. :) Then it was evident we had too many resources. We could force the memory usage down. But we didn't want to give up any of our beautiful resources and effects. Thus some inventive tuning was underway.
The OOM was proven to be an unmeasurable delay somewhere in the Python process. The system kernel had freed the memory (2 GB of images!) after winning a level, but loading more images for the next level resulted in OOM even though the system showed the memory was freed. Not sure what was going on there, but for the sake of time we decided to cut memory requirements by using a rotation step of 2 (180 degrees) for the meteors instead of 1 (360 degrees).
Even at 180 points of rotation, the frame rate still suffered a lot while the cache was building. We fixed that by pre-generating the cached rotations during map loading. But that took 20+ seconds on an Intel i7, and who wants to wait for that? :) Finally we further sacrificed on smoothness of rotation to get the pre-caching performance we needed: bumping the rotation step to 3.
We were still struggling with those issues and more on Thursday. By then we had no game, really. We needed entities and features, otherwise we had a player vs. one dumb baddy in a cave with some madly ricocheting bullets. In retrospect it seems we had tacitly agreed we would make a bullet hell in a cave. I realized this when DR0ID mentioned that is what we had. :D Gumm added sound effects and the game suddenly got fancy. DR0ID quote: "I'm astonished how much difference the sound and sfx can make". Late Thursday it began to come together quickly. But the appearance was still crude, we had no style or consistency, and the screen was full of debugging telltales. I thought we would keep the hand drawn gfx. They were cute and I liked them. But no, DR0ID had other ideas...
Friday was the day it all came together. DR0ID was a whirlwind. The team added entities, features and DR0ID began to work on the final, exceptionally beautiful maps. By end of Friday, we had a great looking game with one map.
Saturday Gumm wrote the story and the pager, and integrated the levels. DR0ID finished the maps. We playtested, tuned and debugged feverishly. (Incidentally, inserting a long delay for the player to read the story allowed the system or process time to free the memory in between levels. This allowed us to push the memory usage back up, so one can run the game--barely--with 360 degree meteoric smoothness. If you're willing to wait for the pre-caching. On the boss level, the game automatically forces the rotation step to 4 because: you *will* be reloading often. ;))
And out plopped a souffle.
DR0ID almost beat the boss level (but he made it, and knows its secrets :)). I could not even get past the first two encounters on the boss level. So I nerfed the baddies by 60% of their health. I left the beefy boss ship alone. Still, I could not beat the boss level. The only way I can win the boss level is to turn on cheats (in settings) and display the walls (F1 in game). Even then I had to retry a few times. Yes, it is that hard. If you win fairly, without cheating, you are pretty heroic in my opinion.
In terms of performance this game is a very impressive powerhouse. I've been studying and optimizing Python-pygame performance as a pet hobby for several years (as I'm fond of telling: ever since they challenged me on pyweek-users: You can't make a scrolling game world, Gummbum, too much pixels for the CPU! :)) thus I know pretty well what the platform is capable of. This game is top tier in processing power. Because you are geeks, here's some info you'll appreciate:
- Images cached @ rotation step 4: 8K images, 494 MB, cache hits 100%
- Images cached @ rotation step 1: 23K images, 1469 MB, cache hits 100%
- Most images have SRCALPHA layer, and more empty pixels than they should
- The background image, and any that don't require transforms are not included in the cache totals
Each game loop:
Idle: 1300 entities (and sprites) total, 301 visible, 215 updated
Peak: 1500 entities (and sprites) total, 700 visible, 500 updated
Collision checks: 10K @ 60 fps, 27K @ 25 fps; 40K (cheating) @ 10 fps