About the Jam
PyWeek is a well established Game Jam focused around the Python programming language. It usually takes place twice per year and is hosted on https://pyweek.org. The 28th edition of it started on September 22nd at exactly 00:00 UTC and as its name suggests, its duration is also exactly one week. During that time, contestants have to build a game from scratch, using the Python programming language.
I’m not going to dive deeper into the rules, if you’re interested about those, you can read up here all about it. There is no reward for the winner, except internet points and being an active part of the community that enables game development with the Python language in the first place.
Every PyWeek runs under a theme, chosen by the competitors in the week running up to the jam, out of a list of 5 possible themes. The theme serves to guide the jam and also make it so, that the contestants cannot just start early.
The theme of PyWeek 28 was Tower. It was amongst my least favorite themes to choose from, but that’s what makes a game jam fun and challenging in the first place.
Warmup for PyWeek
I decided only a few weeks before PyWeek to participate and got lucky enough to take that week off from work. After the decision was made, I knew I would have to do some warmup before it started, since I’ve recently got myself into 3D printing and haven’t done much coding to speak of in months.
With that lack of training, I spontaneously joined a Code Jam a couple of weeks before PyWeek started. I used PySDL2 during that Code Jam though, while I knew I wanted to do PyWeek with Panda3D. So after the Code Jam was before the Game Jam and I started to get back into Panda3D as my specific warmup.
Everyone who has read other posts from me, knows that I’m into Procedural Generation. Something I like to challenge myself with during a jam. I knew that the very first thing I’d need to write a serious amount of code for, would be a generator for some basic 3D shape generation with many bells and whistles (e.g. texture coordinate generation, per vertex normal, tangent and bi-tangent computation, etc.). It was thus clear to me, that the best preparation would be to write such a generator in the days leading up to the competition.
Think of it like riding a bike. Once mastered, never unlearned. But if you haven’t ridden a bike in a long time, you’ll need to get used to riding one again, before it becomes second nature. The same goes for writing code or more precisely for writing particular code. That’s why I chose to write a generator, that way, the second generator in succession would come naturally and much faster, because much less of it is guess work.
Devils Tower
Since 00:00 UTC meant 02:00 AM where I live, my plan was to go to bed early and get up early to start off fresh into the jam week. Unfortunately, my plan didn’t work out that way and I had a hard time falling asleep that night. I was wide awake at 02:00 and found out, that the topic was Tower. So I got up and started off some preparations and get rid of some boiler plate code. After that I finally fell asleep, the time being around 03:30.
During the first day, I tried to come up with something related to the theme at hand. My approach usually involves looking at the disambigouations page of Wikipedia for the chosen word and after scrolling through it and casually clicking on things I didn’t know, the entry for Devils Tower intrigued me. When I saw the photo on the Wikipedia page, I told myself: “You can generate something that looks like that mountain”!
From that point on I started to think about, how to spin Devils Tower into a game. I started a Discord server for my friends to both chime in on and follow along the project. While on one side some brainstorming took place of what I could build, on the other side I knew I need a generator, that can produce rudimentary trees, the Devils Tower and some sort of a character to control, so I set off writing the generator and a small library for handling arbitrary 3D meshes. Having done that extensively the week before, I managed to write most of it in just the first day.
Nonograms and Stone Circles
On the second day, a friend of mine came up with the idea of using Nonograms as puzzle in my world. At that point I already decided, that I want to make something that inspires the player to explore and have a relaxed time while playing. So no hectic action where the players motor and orientation skills would be put to the test, but rather an easygoing adventure in a generated world.
Nonograms played very well into both aspects. You’re not under pressure to finish it and since Nonograms depict images, I could try my hand at writing a symbol generator as well. At that point we were still brainstorming about what other kind of puzzles could be inserted into the game world, or into the “2nd” level… But I had a feeling, that thinking about level two would be a waste of time. The time frame limited to one week and if I learned anything in my previous Game Jam, it is that Murphy and his awful law will be present all the way through. So every time you make an estimation how long something takes to properly build/design/write, double that duration, then add another one of that duration on top. Only then will you, with a bit of luck, have it actually done in that time frame.
So instead of thinking about what level 2 would look like, I decided to use Nonograms as the side puzzles and use the symbols coming out of the Nonograms as the input for the main puzzle.
Enter the Stone Circle contraption. The main puzzle would be to find the correct combination for a mechanical contraption with huge stone rings with lever to move them. Instead of explaining it in detail, here’s an early version of it:
The finished product
With a lot of time wasted in trying to make certain things work (I’m looking at you, normal mapping!!!) the pressure started to build up. Around day 4 I started to seriously consider not entering a game but rather some quirky interactive tech demo thing instead.
I started to prioritize and then remove the least important things I still had planned on my white board. From there on out it was a crunch, but I got things done and around day 6 I started to see a potentially playable game at the end of that week. My neighbor and friend Joel, who happens to be a very talented Bass player, came through on his promise on getting me some ambient music and even some SFX! Unfortunately time was a rare luxury at that point and instead of adding different music for different situations as previously planned, I instead just used one track for music and one effect for motion.
I knew that I wouldn’t come up with a lot of dialogue and chose to give only subtle hints to the player. So after fighting the last few really serious bugs, I finally had a complete-ish game to show for. If you wish to give it a spin, I made an itch.io entry for it as well:
What I learned this time
One of my most important lessons to learn this time around, was to not waste too much time on one single thing. I burned through almost an entire day, trying to get texture coordinate mapping and related to that tangent and bi-tangent computation to work properly. I should’ve left it alone after a few tries and come back later, if there would’ve been still some time left at the end. It was a detail, that in my eyes hurt the overall appearance of the Devils Tower, but at the same time I could’ve accomplished much more important things, that could’ve made the experience much more enjoyable.
I also initially just wanted to code certain things and decided on a whim to write a quadtree implementation, without knowing what to use it for exactly… Once I had written it, I quickly forgot I had it, but when the time came to implement collisions, I found out that for my exact purpose there was no complete solution that Panda3D could offer me at that point (to clarify, there’s plenty around collisions in Panda3D, just not with the combination I was using). This lead me to implement a rudimentary 2D collision system, using my quadtree implementation from before. 2D, because my character could neither fly nor jump, so everything it hits at ground level was enough for my purposes. This went surprisingly fast, though my theory about a collision ellipse was soon thrown out the window, about the amount of time after I should’ve given up on normal mapping.
My conclusion for next time is, to not stick with one thing for too long. If it’s not happening it’s not happening! There’s always a workaround of some sort, that is ready faster and does the trick almost as good. But all in all I’m proud to have managed to deliver a working game and I’m happy with what I learned during PyWeek 28.
well done 👏