JS13K 2020: Planet Not Found

In the last post, I mentioned I may participate in 2020’s JS13K games competition… and I did!

This year’s theme was “404”. I wasn’t really able to come up with a good idea for it, so in the end, I just went and built one similar to the ones I loved as a child. As a Commodore 64 owner, I really enjoyed games like River Raid, Lightforce, Terra Cresta… but I didn’t want to just create a clone; I wanted to modernize the concept while retaining the thrill and playability.

Classic games

I tied the game to the contest’s theme by naming it “Planet Not Found”, given the player will never find any (even though it could be heading to one!).

My first decision was to design the game to be mobile first, which a lot of people use for casual gaming. Some implications:

  • The graphics are relatively large compared to the screen, as the screen in phones is usually a lot smaller than computers
  • The controls are kept extremely simple; it can be played with a single finger, just put the finger where the ship should go and it will be there as soon as it can. The lack of fire button is very easy to solve: just keep firing. The downside is that it’s not possible to have actions, like a special attack or weapon change.
  • Some portable devices may not have powerful CPUs, so most graphics assets are generated when the game starts Portrait orientation, which is the default in phones

Visuals

Because of the size limit constraint, many JS13K games have very simple graphics, or create them in a “retro” style, which helps quite a bit. However, I really wanted to make the game look “modern”, so I looked into procedurally generated ships. After some research I stumbled upon a monthly challenge entry in Reddit that I really liked, so I contacted its author and then created a JS module from it. I may actually look into creating such library from scratch at some point, but I really liked the generated ship’s variety and detail, and its author definitely put a lot of effort into it.

Procedurally generated ships

Even though this library takes 5Kb, it can create ships of basically any size with just a few characters. For instance, the boss is created with:

generateShip(generateFaction("HYj7ADLjQr6icLtO"), "CdiB9N2ZoQWuAxur", 270);

The game uses 14 different ships: the player, the boss, and 12 enemies. Powerups, shield and bullets are generated manually, which is quite easy given their easy shapes.

So now that we have these nice, big ships, what happens when we destroy them? I liked the idea of shattering them, but doing that in regular shapes (like squares) wouldn’t have looked good. Instead, I opted to create a mesh of points that act as the center of each fragment, added a bit of randomness on their position so the fragments are shaped differently, and then group every pixel of the ship to their closest fragment center point. Every time a ship explodes, each fragment is assigned a random speed and rotation, so even if the fragments are the same, the explosion itself will always look different.

Game mechanics

I decided to keep it very simple, with a single “survival” level; it just keeps getting more and more difficult, and a new ship type is introduced every 10 seconds.

The game could have been split in stages, but I didn’t want to put artificial barriers; instead of using extra ships, I added an energy shield, so the ship won’t be destroyed at the smallest touch like most shoot’em ups.

Every 5 seconds a new powerup will appear; it can be either fast fire (orange F), energy shield (blue S) or a bomb (red B).

Approximately every 50 seconds, a boss fight will be triggered. I won’t spoil it, so go check it out; what I can say is that it will get more and more difficult as the game progresses.

What could have gone better

I spent all my available time creating and polishing the game, so it’s not well optimized for size. Perhaps using Rollup instead of Webpack, and Google Closure Compiler with advanced optimizations instead of Terser would have saved quite a bit.

The thing I’m the least satisfied in the collision system; it’s just simple boxes, which is easy to implement, but it can be quite inaccurate in some situations. So, with a bit more time I would add a “second pass” that validates the collision with the actual image pixels.

Last, I should have implemented keyboard controls to provide a different experience and potentially better accessibility. It would take a few extra bytes, but didn’t think of it until after the context.

Closing thoughts

Participating in this contest was really enjoyable, and a big change compared to my past experiences with JS1K, where optimizing the code is probably the most satisfying and time consuming part. It took quite a bit of time and effort, but I’m very happy with the end result, and I’ll be definitely playing it from time to time.

Play it here, and the source is on GitHub.