SUBmersible WARship 2063: a JS13KGAMES 2018 postmortem

Another year, another edition of JS13KGAMES, this month-long game development competition in under 13Kb of HTML/CSS/JavaScript that runs from August 13th to September 13th.

This year I made SUBmersible WARship 2063, a 2D arcade game paying tribute to 3D submarine simulator Subwar 2050 (Microprose, 1993).

Image for post
Image for post
Enemy submarines have entered your perimeter. Stay off their radar and fight back!

Play SUBmersible WARship 2063 either on JS13KGAMES or on my website herebefrogs.com/submersible-warship-2063.

Also have a look at my past postmortems for Blade Gunner (2016) and A Tourist In Paris (2017).

Game Ideation

If the theme permitted it, I hoped to submit the same game for 2 other game jams which overlapped the JS13KGAMES competition: DeMake jam (redo an existing game with more limitations), and A Game By It’s Cover (create the game for one of the fictitious game cartridges of My Famicase Exhibition).

“Offline” reminded me of submarine sonars in a 1993 simulator called Subwar 2050 by Microprose: in active mode, sonars broadcast a powerful signal that gives you precise enemy locations but at the same time betrays your own position. In passive mode, they only listen to ambient noise, which renders you mostly blind but as the same time virtually invisible to enemies. I liked the inherent tension of that gameplay (each sonar mode has drawbacks which balance its advantages, prompting the player not to stay indefinitely in one mode over the other). Subwar 2050 was a 3D game, which meant that if I turned it into a 2D game it would also qualify for DeMake jam.

Another idea was a survival game inspired by the movie The Martian and a Famicase cartridge called Open World. The player is stranded with little energy left to power his equipment, and needs to make crucial choices about which subsystems to keep online and which to turn offline to progress. I imagined a satellite view that would take the player to different artifacts and vehicles, slowly increasing his energy store and collections of subsystems.

The submarine game felt more accessible while the survival game would demand a strong storyline and graphics, so I went for the former idea.

Art direction

I’ve admired the simplicity and clarity of tactical maps from Netflix’s anime Knights of Sidonia, and since I was planning on making a 2D game, this style felt a good match.

Image for post
Image for post
Actual Knights of Sidonia screencap. Did I rip off the style? No, I paid tribute ;p

Pixelart wouldn’t work here so I quickly experimented with vector art in the Canvas API.

Adding a colourful blur, typically used for dark shadows, gave a nice monitor glow to the submarines and torpedoes. Fill patterns allowed me to create that grid of + which gave the map a very tactical and military look. Dashline offsets were also a cheap way to animate the radar range around the player submarine. I would have loved to push UI animations to the levels seen in Knights of Sidonia, but ran out of time.

Lessons learned

Use a build chain

“How do I package my game for submission?” is a question that came up several times on the JS13KGAMES Slack channel throughout the competition. I like to figure this out early on and be able to submit at a moment’s notice, rather than scramble at the eleventh hour and risk missing the deadline.

My principles on build chains are as follow:

  1. Packaging the game must be automated: I will need to do this a lot over the course of the competition, and I want to save time and eliminate the risk of manual errors.
  2. Any change to the source code should repackage and reload the game in my browser: this is 2018, and I vowed to never trigger builds by hand nor push that Reload button ever again.
  3. I should test the same version that I am going to submit: I’ve been burned by testing the development version and not realizing that packaging optimizations had broken the submission version, so the only differences between the 2 versions are now the exclusions of sourcemaps and livereload script.

I used a combination of modern web development tools, refined over the years:

  • Rollupjs bundles my scripts. It knows how to handle import/export statements so I can structure my game into separate ES6 modules. It only bundles functions that are actually used. And unlike Webpack which adds a lot of extra code to the bundle to load your modules, Rollupjs simply inlines all your functions as globals. It can wrap them into an IIFE so the minifier can do an even better job, and generates sourcemap to help you debug in the browser (since the bundle code you run is structured differently than the source code you wrote)
  • Terser minifies my code. Terser is to ES6 what UglifyJS is to ES5. It will also update the sourcemap produced by Rollupjs so you can still debug your game as if its source was plain text. But don’t just take my work for it:
  • I don’t have much HTML/CSS in my codebase (less than 0.3% according to GitHub) but I optimize it nonetheless with htmlmin and inline the JS bundle inside index.html.
  • I zip the resulting HTML page, and optimize the archive again with AdvZip (part of AdvanceCOMP — available on Mac via Homebrew), which shaves off up to 1000 bytes.
  • I output the Zip size to the console, as well as the number of bytes and % left of the 13kb budget so I always know where I stand.
  • Rollupjs watches my source files and trigger the build chain on every change. Once the build completes, Browser-Sync instructs the browser to livereload the latest game build.
  • For extra points, I can shrink my images and tilesets with TinyPNG (not used this year as I had no pixelart).

And that, my friend, is how modern web game development is done! Check out my complete build script (I will try to rewrite it into a simple bash script at some point)

Plan your music ahead

Once Submersible Warship 2063 started being playable, I contacted music artist Mark Sparling. Mark creates chiptunes every day as a self-improvement exercise and post them on Twitter:

He was able to roll with very limited creative directions (“a low & eerie ambient music, maybe a tad oppressing, to evoke the vastness of oceans depths, and the "bleeps" of submarines and torpedoes echos”) using a tracker he wasn’t accustomed to, nonetheless. The track he composed is just magnificent and pulled SUBmersible WARship 2063 to an entirely different level.

Soundbox itself was pretty easy to embed in my code. The only surprise which wasn’t obvious from the docs was that CPlayer.generate() returns the percentage of the song loaded (in range 0–1) so you have to keep calling it until it returns 1. The demo uses a song so short that it loads in one call, therefore its code doesn’t bother looping, but it’s actually necessary since a partially loaded song isn’t able to playback.

const player = new CPlayer();
player.init(/* song data */);
let loaded = 0;
while (loaded < 1) {
loaded = player.generate();
console.log(`loaded ${loaded * 100}%`);
}
let wave = player.createWave();
...

Generate() is also very slow on Safari for reasons unknown.

Entity-Components-Systems

ECS is a way to structure your game logic so all your actors abide by the same rules that govern your world. I had unknowingly designed my first game, Blade Gunner, around such a system but Florent Cailhol formally introduced me to the ECS theory:

  • An Entity is an actor in your game (e.g. player and enemies…) and is modelled by a collection of components.
  • A component is a group of properties for an aspect of your game world (e.g. x/y coordinates for a Position component; vx/vy for a Velocity components; up/down/left/right for a InputControl component). I used to keep all my actor’s properties into a single object, but splitting them up in separate objects helped keep things organized.
  • A system is a function that applies a rule of your game world. It consumes some components and may generate others (e.g. applyInputToVelocity() transforms keyboard/gamepad inputs into player velocity; applyVelocityToPosition() updates each actor’s position according to their current velocity)

The game’s update loop runs every component through the relevant systems. It makes it very easy to add or modify game rules. For example, midway through the competition I decided to change the submarine controls to make them more realistic. I went from moving the sub according to the direction of the arrow keys (vertical/horizontal/diagonals) to moving it forward/reverse or steering left/right. All I had to change was how applyInputToVelocity() converted the keys pressed into a velocity vector, and all the other systems after this one (position, collision, torpedo firing, rendering) kept working consistently.

What I’d do differently

Mobile first gameplay

Once again, the competition’s Desktop category saw 200+ entries, whereas the Mobile one topped around 60, so it’s easier to have your game stand out in the later. Unfortunately, I didn’t get to enter SUBmersible WARship 2063 under Mobile due to gameplay decisions: steering the sub requires the directional keys, and although I had managed to approximate a decent DPad with touch control in a previous experiment (the player would move in the same direction that your thumb was going on screen) I was still missing a mobile alternative for toggling the sonar and firing torpedoes.

Next time I will focus on creating a mobile gameplay first, so it will be easier to translate touch controls to desktop, than the other way around.

No AI

AI is freakin’ hard, and a gameplay that relies entirely on computer opponents behaving convincingly as human is just a lot of work! I kept pushing enemy AI till I had figured out how to make torpedoes lock on targets, assuming I could reuse that same strategy to make enemy subs engage the player. But I had given zero thought about patrolling behaviours. And to think that I also wanted some enemy subs to go offline to elude player torpedoes and sneak on them…

With little interesting content to feature in even a single level, I had to get creative to save the game’s campaign mode. I created a new type of enemy requiring simpler behaviours: mines don’t move around and will only fire at the player if they’re within range. I designed the first level as a tutorial mission, placing an underwater rock between the mine and the player sub to create a safe playground for the player to discover the sub’s controls without risking immediate death. The second mission features enemy submarines, and is the most embarrassing: enemy subs patrol by picking a random destination on the map. It looks quite convincing until you wait long enough and out of harm’s reach. As I didn’t implement obstacle avoidance, enemy subs will end up crashing into each other or on the rocks I added to make the map feel less desolate. The last mission features a “disabled” submarine (repurposing an AI strategy that didn’t get updated to the new input control scheme and produces erratic motions). The player has to best half a dozen mines to get close enough to sink its quarry.

Next time I will avoid gameplays that rely on intelligent behaviours.

Asset editor

Since I wasn’t using pixelart this year, I hadn’t given a thought to how I would create all the vector graphic assets. If submarines and torpedoes were really simple — a rectangle overlapping a square or a circle — , underwater rock formations turned out to be a headache. Given I was running out of time, I constrained rock shapes inside a circle so collision detection between a sub and a rock could be done by comparing their centres’ distance to the circle’s radius. I drew the shape on a notepad and calculated the vertices coordinates… by hand, which greatly limited my ability to vary designs. I ended up rotating that one shape to break uniformity.

Image for post
Image for post
Behold the One Rock’s secret formula!

Other competitors, like Madmarcel, had the right idea and dedicated some of their competition time to create an asset editor (available on Github), which greatly sped up their content creation and import in-game:

Next time I need a lot of custom assets, I will either reserve some time to create an editor or assess existing tools that would suit my needs.

Grade 9 trigonometry

To keep things simple, I limited all my collision detections between subs, rocks and torpedoes to circle-to-circle. It’s a comparison of the distance between the centre of the 2 entities to the sum of their collision circle radius:

areEntitiesColliding = (r1 + r1)² > (x1 - x2)² + (y1 - y2)²

The most important part of Submersible Warship 2063’s gameplay was the ability for torpedoes to adjust their course toward enemy subs entering their cone of vision (the same calculation would also allow enemy subs to track the player). Therefore it was critical that I could tell, given the velocity vector of the torpedo and the position of a target, if the target was to the left or to the right of the torpedo from its point of view, so it could steer closer to the target… A simple application of trigonometry, yet waaaaaaaay too practical to be covered by my Grade 9 teacher, who preferred academical problems involving triangles of all kinds but definitely lacking in the torpedo department.

After scribbling a non-negligible number of useless diagrams, I turned to Google to find and understand the formula:

// (tx, ty) is the target’s 2D position
// (sx, sy) is the source’s 2D position
// (vx, vy) is the source’s 2D velocity vector
// alpha is angle in degrees in range [-180, 180]
alpha = (((Math.atan2(ty - sy, tx – sx) – Math.atan2(vy, vx)) * (180/Math.PI) + 180) % 360) – 180

Here is why it works:

Image for post
Image for post
Torpedo position & velocity, and sub position in a 2D space. Alpha is the angle we’re looking for.
Image for post
Image for post
atan2(vel.y, vel.x) is the angle between the torpedo’s current direction and the x-axis
Image for post
Image for post
atan2(tar.y — src.y, tar.x — src.x) is the angle between the line passing by the torpedo and the target, and the x-axis.
Image for post
Image for post
The difference between these 2 angles (modulo 2PI) is the angle we’re looking for in the range [0, 2PI]

Next steps

There are a couple of ideas I had to drop that I might end up implementing after the competition:

  • enemy subs that can turn its sonar offline to disappear and evade your torpedoes,
  • levels larger than the actual screen, with camera scrolling keeping the player sub roughly in the centre,
  • poor man’s 3D with 2 parallax layers (one in front of the game map moving faster, and one behind moving slower),
  • when the sonar is offline, a proximity alert revealing enemy subs or torpedos extremely close to player sub (justifying the dotted circle revolving around the player sub),
  • caching the game with service workers so it can be played offline.

My favourite JS13KGAMES 2018 games

go OFFLINE is simply the game I wish I had done this year: 100% on theme, simple and sleek.

UNDERRUN is an impressive exercise in 3D.

The story of THE CORE is very cute.

The Chroma Incident (which I helped naming, incidentally ;p) is Nuclear Throne meets Super Crate Box meets Splatoon meets pixel dithering. I haven’t seen textures like this since Coreldraw in 1996!

I love the simple pixelart of The Matr13k and marvel before the pixel perfect collision detection.

Everyone’s Sky is a whole universe to explore in 13kb and that’s just impressive.

Bellwoods is very poetic and the fluidity of its mobile controls is just exemplary.

Hans Fixes the Internet is something out of this world that you’ve just never experience before.

I love the simple pixelart of Castle Escape and even though the author states the game is unfinished, I was caught up by the story and had to complete it.

Envisionator is a smart and compelling puzzle game whose level of polish rivals commercial titles.

Bulletcraft has a nice Super Crate Box feel to it. Bullet time and 1 byte pixelart, what more could one ask for?

Written by

Software engineer turned manager and father of four, I create pixel art, video games and visual experiments on the Web.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store