Building a Pseudo-3D Racing Game for the 2020 JS13k Game Jam
The unique twist about JS13k is that the deliverable at the end of the month-long game jam is a sub-13k zip file containing a single index.html file. This 13k limitation is tricky because it has a way of feeling weirdly roomy at first before becoming maddeningly constrictive during the last few days of the jam. Additionally, in contrast to shorter 48-hour game jams, JS13k runs for an entire month, which may expand the types of projects a dev is willing to take on. In this post, I’ll go over how I put together my pseudo-3D project for this year’s game jam and add a few tips I’ve picked up from doing this game jam over the past few years. My game this year is called Vote by Mail: Funding Not Found, and the source code is available here.JS13k 2020
I’ve participated in JS13k twice before, first making a 2D mini-RPG using HTML canvas in 2018 and then coming back to make a Missile Command-style game using WebGL in 2019. This year, I knew I wanted to make a pseudo-3D style racing game in the vein of late 1980s racing games like Out Run. In the pursuit of this goal, there were three big challenges I set for myself: 1) drawing a scene in that pseudo-3D style, 2) creating my own pixel art, and 3) creating my own sound effects “manually” with Web Audio API.Creating a Pseudo 3D-style Game
With short game jams, there can be pressure to use techniques you’re comfortable with to increase the chances you’ll be able to finish the game on time. However, since JS13k runs for an entire month, those fears are somewhat relaxed and the jam can be a great opportunity to learn new techniques. This year, I wanted to pick up the pseudo 3D racing game technique, and fortunately there are already at least a couple great resources on this topic (two examples: Lou's Pseudo 3d Page, How to build a racing game). While the math in these games seems deceptively simple and never gets more advanced than what they attempt to teach in high school, it can still be pretty tricky! For my part, I never quite pulled off an effect of Out Run quality, but I think I got the basics working. Based on Lou’s suggestion, I used a “Z Map”, which maps each line of the road on the screen (i.e. the horizontal line on the screen) to a height in the imagined distance (i.e. the height of something in the game world), which was then used to both scale (i.e. change the size of) the lines on the road and the sprites on the screen. I’ll let Lou’s page do the explaining in more detail, and here’s a link to my code where I do my implementation. The good news is HTML canvas’ API makes drawing lines relatively easy so once you have the Z Map built and sort of understand how things fit together, drawing the road to the screen is isn’t too tough.Making Pixel Art
I don’t have a ton of experience making pixel art and wasn’t sure if I could make everything I needed for this game. However, I had read somewhere that tracing was a fine way to make art, so I downloaded an image of a mail truck from Google Image Search, loaded it as a “reference image” in Aseprite (check out this link to see how to do that), and traced over it as best I could as in the image below: It won’t win awards, I think it looks a little like a mail truck! That gave me enough confidence to try the same tracing technique with a mailbox, the White House, and the city of Chicago. After that, I followed a tutorial to make a pixel art tree, which wasn’t super successful, but got me something. Finally, I did my best to make a brick wall, gold coin, and an envelope.Sound Effects
Granted, there are already several (likely better!) ways to create sound effects appropriate for JS13k that are less laborious than rolling your own effects in raw Web Audio API (a number of people have had great success with ZzFX). However, I wanted to learn a little more about how Web Audio API works. More specifically, I wanted to see if I could create my own version of the Battle Hymn of the Republic. To get started, I read this tutorial that describes how to create oscillators in Web Audio API. Next, I found this link that has a mapping of frequencies to notes, which I saved for future reference. After that, I was lucky to find this wonderful person who had helpfully created a YouTube video demonstrating a super simplified arrangement of the Battle Hymn of the Republic in which they also listed the notes they played. With all this information, I had everything I needed to play my own stripped down, pretty janky version of the Battle Hymn of the Republic (the code is here). Like the pixel art, it won’t win awards, but I was happy to have put together something! If you play the game, you'll hear the song at the very beginning. One gotcha with Web Audio API is that every time you stop an oscillator node, you may hear a jarring “click” or “pop” sound. After reading various posts on the web from people who had this problem, it seems like the best solution is to use the “setTargetAtTime” or the “setValueAtTime” method on the gain node to fade out the sound over a short duration so it doesn’t have a sharp pop right at the end. I found this a bit cumbersome, but it did work.Tech Choices That Helped
There were a number of tech choices that I think were helpful in keeping the development process from being super painful. Specifically, I think using TypeScript and having a robust build system with Webpack made my life easier down the stretch when time was tight.TypeScript
I think using TypeScript was a good choice for a lengthy, month-long project like this. Since I didn’t write tests for this game, there were many opportunities for regressions as I refactored my code over the course of a long month. For example, when I refactored a type called “RoadSegment” to have a property called “id”, it was helpful to have the compiler let me know when all the instances of “RoadSegment” were updated. Had I gone with JavaScript, I might have missed an instance or two, which would either have led to a hard-to-track-down runtime error or perhaps just weird behavior in the game.Webpack
I don’t think it’s controversial to say that Webpack is pretty confusing to set up. For several projects, I’ve avoided it (opting instead for something with a more batteries-included setup like Parcel) mostly because of the headache involved in doing that initial configuration. However, I decided to take the initial set up hit this time around, and I don’t regret it. The main reason I appreciated Webpack for this project was that when I needed to do some customization (mainly around injecting the JavaScript and CSS into the index.html file), it was possible if not always easy. Additionally, plugging in alternative optimizers like Terser was straightforward, which saved me a couple dozen bytes in the last couple days of the jam and was critical for my staying under the 13k budget.Compression
Figuring out how to shave off a byte here and there can be a frustrating experience especially because there is so much translation between the code that is written and what eventually gets packed into the zip file (e.g. TypeScript->JavaScript->Minified JavaScript->Zip file). Attempts to reduce the file size can have surprising results too: I’ve had cases where moving duplicated code into a function (in essence removing lines of code) has actually increased zip file size. So if you’re in the position where you’re trying to reduce the file size of your game, make sure you have the basics in place first:- Your JavaScript and CSS are minified
- Your zip is compressed at the highest level
- If you have image files, try using pngcrush
- Are you using some sort of canvas-based text? Can you delete letters you’re not using?
- Double check that you have no unnecessary HTML tags ore CSS rules
- Consider trying to a different JS or CSS minimizer/optimizer--For me, Terser ended up being better than the built in Webpack one
Rounding It Out
Maybe the most important part of getting through any game jam is not getting discouraged. JS13k is no different, even with its unique constraint of delivering a web-based game in under 13k. If you can forge ahead and keep your game scoped to something manageable--congratulations!--you’ve completed the hardest part of the game jam! One last thought: about a week before JS13k finished, I used the #js13k hashtag to ask folks on Twitter if they could provide feedback on an early build of the game. A few people responded, and each response resulted in a meaningful change in the game. For that reason, I’d recommend trying to finish a playable version of the game as early as possible and start pulling in feedback--it made a huge difference for me.If you liked this content follow us on Twitter for more!