I first heard about the Winnipeg Game Jam (or Peg Jam as it is affectionately known as) in 2013 or 2014. Despite wanting to attend every year I wasn’t able to do so until 2017. That year the theme was “nothing is as it seems”. Despite having some limited Unity experience I decided to go big and create a third-person view 3D puzzle/story game called Welcome to Pelletown!
The story was about a cute little capsule named Pilar who lived in Pelletown; a world of anthropomorphic shapes where everyone is happy, everyone runs the maze, and everyone eats pellets dispensed from dispensing tubes. Running out of pellet power caused you to be returned to your starting position.
Ultimately if Pilar explored the maze a little deeper she would find a secret room and a floating ball of energy that would help her disable her “regulator”; a normally-invisible device strapped to the side of her head that reset her when she ran out of pellet power. Free from the regulator, allowing her pellet power to run out allows her to perceive her world the way it truly is:
The rest of the game was attempting to escape Pelletown, all while dealing with the fact that certain aspects of the game were only available with and without pellet power (similar to the game mechanics of Legacy of Kain: Soul Reaver). The goal was to provide a variety of puzzles that allowed Pilar to progress and gradually reveal the story-line but it didn’t get further than Pelletown proper.
Between bad camera angles and lack of sufficiently compelling NPC dialogue most test players never realized that there was more to the game than running the maze. It did have its moments, though.
The reason I’m talking about Welcome to Pelletown is that the game was too big to be made in two days. And with my life and the demands on my time, there wasn’t going to be continued development of Pelletown. For me, what happens at Peg Jam, well…ends at Peg Jam. So the important lesson I took away from the experience is to keep it simple.
Getting the green light from my wife to abandon my familial responsibilities for an unheard-of duration of an entire weekend and attend Peg Jam 2019 I decided to approach the jam differently this time around.
Preparation
I had done a bit of playing around with Unity over the past 4 years, but several months ago I stumbled upon three.js and vowed to look into it when things with my major pet project Drawn had died down. Of course I lied to myself and didn’t give three.js a second glance.
All that changed on October 24th when the planets weirdly aligned for me. I had lunch with an old schoolmate named Hubert that I hadn’t seen in 20 years and the topic of discussion was new media and potential collaborations. Coincidentally that afternoon a subreddit I subscribe to for web developers posted a link to the brilliant portfolio website of Bruno Simon, which was written entirely in three.js. I shared the link with Hubert along with the statement: “I’m pretty sure I could create something like this”.
OCD is a hell of a drug and because of what I said to Hubert I felt compelled to actually see if I could do it. By the end of the following day (about 16 hours work in total), I had a much uglier and buggy clone of Bruno’s basic portfolio mechanics, but more importantly I had proved to myself (and Hubert, for whatever that was worth) that I could do complicated things quickly in three.js.
I then thought that it’d be cool to create a 3D portfolio site like Bruno’s, but I wanted to do something more unique and interesting to me. I always loved space and space games like Freelancer, so I decided to play with the idea of a portfolio where each planet was one aspect of my CV. I stole the cute robot model from the three.js examples and made him a character who moved where you clicked/tapped, turned to face you and waved if you clicked on him, and could board a rocket to fly between planets in a small circular universe. I decided that to make it intuitive the rocket would also simply follow the mouse/finger location and accelerate on mouse/finger down and decelerate on mouse/finger up. I dubbed it “spaceport” for “space-based portfolio”.
All three.js examples are in terrible procedural code at the body of the HTML documents for each example (though I guess it’s to keep it as straightforward as possible) and so too was my Bruno Simon portfolio clone. To improve on it I started creating JavaScript classes to encapsulate my “universe”, and the various levels/stages/planets to make it saner. After 8 hours I had base classes for planets that the robot could walk around on…
…and a “space” level where you flew the rocket between planets in my fictional star system:
I had stumbled across a site (I can’t find the link now) that allowed you to create a lovely starfield effect in three.js that you could easily animate in any which way. The whole thing was less than 20 lines of code so I quickly adapted it to my space level.
With such a quick escalation of abilities in three.js in just one week I decided I would attempt to create a game in three.js for Peg Jam. Not knowing the theme (it’s always a surprise until the end of the first night) I had to think about what type of games I could create. Single player games either require complicated puzzles (which could take a long time to come up with and create), complicated AI adversaries or NPCs (again too hard to create in the time allowed) or dumb AI adversaries (which get boring fast). Some of the most interesting games are multiplayer. But could I do multiplayer in three.js?
Enter socket.io
In 2016 I worked on a web/mobile project that involved having to build a realtime push notification system with a chat component. It was decided that we would roll our own so I took a crash course (i.e. Google) in node.js and using the express and socket.io packages I cobbled together a functional push server. Then earlier this year the 3rd-party push notification service I was using for Drawn failed so I once again turned to socket.io to create my own service which is how Drawn currently coordinates realtime notifications.
I wondered if socket.io could work for a multiplayer game in three.js. After all, with three.js all the work is done in the client so the server is free to do nothing but exchange messages. With only a few days before Peg Jam I recalled that Visual Studio 2019 has node.js support so I started a node.js project, added the express and socket.io packages, used my limited Blender abilities to create a basic “person” character, added my new “base classes” for three.js world rendering (now refined to automatically provide a loading screen for preloading game assets), and very quickly wired it all up so that the players all saw each other and updated their movements in the “game”. The joy of using Visual Studio 2019 was the it handled everything and even did a pretty decent job of providing intellisense and autocomplete in JavaScript, which is a feat unto itself.
During this process I learned that gltf models created in programs like Blender can’t be reused multiple times in three.js unless they are cloned in a very specific way (which doesn’t clone their materials), so I adjusted my fledgling class library to handle that properly.
With proof of concepts for anything I could want to do under my belt I was now ready for Peg Jam.
Day 1 (Friday, November 1st)
The first day we were treated to presentations by a variety of speakers from the industry. The first was Dan Blair of Bitspace Development who gave a very frank and heartfelt talk about the perils, professional and personal, of running your own studio. His talk resonated with me as I’ve feared a lot of what he’s gone through which is why I have a regular day job instead of running my own shop.
Dan was followed by Rebecca Harrison. She was incredibly upbeat and gave the very useful advice of “try to describe your game without referencing another video game”. I had a very nice chat with her at the end of the evening.
After Rebecca was Corey King from ZenFri. Along with the crazy hair was a crazy energy and I could tell how supportive he was of the community as a whole. I wish I had a good enough idea to take him up on his offer of assistance in getting funding.
The final presenter was Matt Woelk from Ubisoft Winnipeg, who I almost ended up working for/with last December. His talk focused on the networking and collaboration that frequently happens during the jams.
Once the presentations were complete we the theme that had gotten the most beans in its jar was revealed (we all got to put a bean in one of several jars labelled with a theme): “Parallel Worlds”. It was the one I had put my bean in. 🙂
To team, or not to team?
Game jams are heavily about collaboration and networking. I had invited a few friends to attend with me but they were unable to make it, thus I had the disadvantage of not really knowing anyone there in any capacity (aside from a few people I had met at networking events over the past couple of years). My sometimes crippling introversion didn’t help either. I briefly considered being paired up with someone random to form a team but while I work well in teams, I’ve always found that I do my best work on my own. So lone wolf it was.
Deciding what to create
Once I had decided to go it alone I withdrew into the back corner of the space (coincidentally where I was going to be sitting the next two days) and opened up a note-taking app on my phone. The first things I wrote were “Game Jam 2019”, “Theme: Parallel Worlds”, and as a mantra “KEEP IT SIMPLE”.
I’ve always wanted to build a video game where I’m the main character (I tried writing a low-res graphics game called “ClayQuest” on Apple IIe computers back in 1992) and since the late 90’s I’ve had an idea for a game about an “evil” version of myself from another universe who uses a portal gun (that’s right, I had the idea of a portal gun before Portal and Rick & Morty, so there!) to travel to alternate universes, using the different layouts of buildings/nature between realities to navigate the maze (again similar to the game mechanics of Legacy of Kain: Soul Reaver). Obviously any game like that would be too complicated for a two-day jam and I wanted to avoid the mistake I had made in 2017.
After writing a couple of other random things, I very quickly settled on the idea of worlds (planets) being physically parallel to each other. That quickly tied in to what I had been experimenting with over the past week and a half, and bringing to mind simple popular games of the past (like Frogger) it all fell into place:
Since I came to the first night straight from work and my computer was at home in Selkirk, Manitoba, there was no point remaining any further and I returned home. Determined to get a good night’s sleep to start fresh in the morning I couldn’t help but open up Visual Studio 2019, start a new Node.js project entitled “Parallel Worlds” and include the express and socket packages.
Day 2 (Saturday, November 2nd)
Back in 2017 when I arrived at the designated start time I discovered they had opened the doors early and others had gotten an early start. Determined to take advantage of every moment available to me, this year I arrived at the ACE Project Space at 321 McDermot 20 minutes early. Unfortunately this jam was holding strict to the “10am doors open” policy and I was quickly joined by a large group of early birds waiting to get in. Fellow jammer Tamara had fallen on her way to the venue and tore up her knee pretty bad. Fortunately I was carrying a first aid kit on me and was able to lend it to her so she could patch herself up.
Once we were allowed to come in from the cold I set about unpacking my PC and monitor from the wheeled luggage I had carefully packed it in, only to discover that when I removed the monitor stand and set it aside I had totally forgotten to bring it with me! It took me a bit of MacGuyvering and rotating the screen 180 degrees, but I was eventually able to set up something that worked:
As my new three.js class library contained built-in logic for a load screen, the load screen was the first thing I created, utilizing the starfield effect I had found earlier, and rotating it on all three axes to keep the relatively static landing page interesting.
Since three.js is a JavaScript library that renders in a <canvas>
tag, there’s no need to create any sort of convoluted sprite-based UI layer; simply overlay the canvas with regular HTML objects. Adding a style of pointer-events: none;
to elements that are information-only allow pointer events to fall through to the canvas. As a web developer this allowed me to use Bootstrap and jQuery to quickly whomp up the menu system which consists of three primary options:
The “Play Online” and “Practice” options would both ultimately lead to the same “level” class (called “PlayField”) and the “About” button simply showed a different HTML container with information about me and the music license attribution for the ubiquitous Kevin MacLeod.
I already knew how to create the planets from my experiments with “spaceport” (above) but the rocket I created there was done purely in code and I wanted something nicer. So I used my limited Blender abilities to cobble together a somewhat respectable rocket:
Then I got to work coding and testing. The starfield and the two planets on either end of the field of battle were the first things to be created and I had them up and running in less than half an hour. Next came the asteroids, which I used a dodecahedron primitive in three.js textured with a cratered moon texture and coloured brown. At a certain time interval the update loop generates a randomly-sized asteroid at random coordinates within a certain rectangular area just off the bottom of the screen, adding it to the render scene and to an array of game objects for the level. Existing asteroids are moved upwards, and once they are beyond the top of the screen are removed from the scene and the game objects array. This keeps the number of objects the engine has to deal with consistently between 60 and 80.
The rocket mechanics worked exactly like my prototype in spaceport, except that in Worlds in Perillel the rocket thrust is always firing and you can only direct where it flies (vs. spaceport where thrust stopped once you released the mouse/finger). Thus it didn’t take long at all to have that working.
Collision detection ended up being more challenging than I thought, despite three.js having the Box3 class for effectively managing collision detection. After at least an hour of time spent trying to figure out what I was doing wrong I finally utilized the Box3Helper class (which renders the collision box in the scene) and discovered that something in my rocket model’s gltf file was causing the box to extend at least 3 times the length of the rocket on the y-axis. I changed the box logic for the rocket class to use only the fuselage of the rocket rather than the whole model and suddenly everything started working. I added code to “kill” the rocket and the asteroid and remove them from the playing field after a collision.
I initially had a keystroke tied to launching a rocket for testing purposes but eventually built a hard-coded “cool down” period where you have to wait before the next rocket can be launched and added an HTML overlay at the top of the screen showing the planet’s health and the countdown to your next launch. Again I used pointer-events: none;
to prevent the overlay from capturing any pointer events away from the canvas.
With the practice field mostly working I decided to turn my attention to the multiplayer aspect. I decided it made the most sense to keep as much logic as possible on the clients and minimal logic on the server, so multiplayer works like this:
- A new player enters the “lobby” and provides their name. There are no other players in the lobby, so the server assigns them a randomly-generated game number and they sit there waiting for an opponent.
- A second player enters the lobby and provides their name. The server checks and sees there is one person waiting so it assigns the second player the first player’s game number and on the server side connects each socket to the other with a new “opponent” property.
- When game information (such as movement, collisions, etc.) are received from a socket, it is automatically relayed to the socket’s opponent (no need for special channels).
- When a socket disconnects, if it has a non-null “opponent” that opponent is informed that their opponent has backed out of the game.
On the client side, a SeedableRandom
class (that I created based on this solution) is seeded with the game number. Thus both players are using the same random number generator to determine where to generate asteroids, regardless of what device they’re on.
When I finally powered down at 9:45pm, I had almost all of the single player practice and multiplayer game mechanics functional, minus win condition detection. I packed my everything back into my luggage, gave fellow jammer Kai a ride home, and returned to Selkirk.
Day 3 (Sunday, November 3rd)
I didn’t bother removing my PC from the luggage at home, as I’m the sort of developer who does most of his work in his head and I’m also lazy. After a half-decent sleep and a shower I had a running list of solutions to several small, nagging problems, and after dealing with several small, nagging children I packed up the car again and returned to Winnipeg.
Running late I arrived sometime around 10:30am but quickly got myself set up. Given the awkwardness of the monitor stand and the fact that my workaround yesterday was acceptable I left the stand at home and once again worked with my monitor upside-down and leaning against my tower.
The first thing I did was open Notepad and create a TO-DO list, consisting of things I needed to get done, and “nice to haves” if I was able. I then dove in and started changing the hyphens to ‘X’s as I completed them and sometimes moving things down to the “nice to have” section. By the end of the day, this is what my TO-DO list looked like:
Sound effects came late in the game development life-cycle. I had picked out the music for the game (Kevin MacLeod’s Jellyfish in Space) Saturday morning while eating breakfast but had disabled it in the game because it was getting annoying listening to it for hours on end. Wanting to not lose too much time on searching for sound effects I downloaded Bfxr and kept clicking on presets and adjusting dials until I got all the sounds I wanted. Since Bfxr exports in wav format I used Audacity to convert it to mp3. The screaming you hear when a planet is hit was an excerpt from this video, where I grabbed a random chunk and applied fade-in and fade-out effects in Audacity.
The very last thing I did was add the explosion effects, and I figuratively did that at the last minute (I started it around 5:00pm with presentations starting at 5:30pm). Three.js lacks a built-in particle engine and the only examples I found online by others were for earlier versions of three.js and were incompatible with the version I was using. Not wanting to spend the time trying to figure out how to upgrade someone else’s code I initially feared I’d be demoing it without explosions but I decided to just spawn a rapidly expanding and rotating sphere with an orange-coloured swirling water texture. It’s not great but it conveyed the effect appropriately enough.
When all was said and done, I wrote over 2,400 lines of code and created three rocket models from scratch. I managed to organize my files pretty well, though admittedly the JavaScript might have been cleaner with subdirectories…I just didn’t want to have to deal with all that.
The end result
In my day job I frequently lead meetings and give demos and presentations but I was quite nervous about showing my game off. I was primarily worried that some bug I missed during testing would appear in the demo (as they often do) and make it look terrible. I was worried that I’d talk too fast (which I do when I’m nervous) or “uh” and “um” and forget to mention key things.
As demos go, however, mine went really well despite the fact that I spoke too fast and “uh”ed and “um”ed too much. The crowd seemed to like the game and were certainly impressed with what I achieved in two days. My Peg Jam neighbour Nic was kind enough to record my presentation with my phone, and while the audio isn’t that great, here it is:
And as promised in the video, I spun up a node.js server on the same server that hosts Drawn, so now you and your friends can play it too! Click on the image below or check out the game’s page on itch.io. Check out the other amazing Peg Jam 2019 submissions!
So why do I love these game jams? At the end of the day, I think it’s about the fact that I’m given a concept and allowed to do what I want with it, which is when I shine as a software developer, and sadly is a situation I don’t find myself in very often these days.
Thank you so much to New Media Manitoba, Daniel Voth, and everyone else who helped make this event so much fun! I’ll be back next year, if my wife lets me!