GameDev

js13kGames 2014 post mortem – I finally finished something!

js13kGames 2014 is now over. It was my first bigger gamedev compo. I wasn’t really sure what to expect – my only goal was to finish my 13kB entry and submit it on time. The number of entries this year was impressive – 129 – it’s like 2012 and 2013 editions combined! One of those entries was mine and what’s even cooler – it’s got 12th place!

The idea

The theme for this edition was “The Elements: Earth, Water, Air and Fire”. My first thought was making some kind of alchemist game, but this was too obvious and I was pretty sure someone will eventually make a game like that. Besides, I’m not a huge fan of this type of games.

Then I thought – why not to fight fire on earth with water from air? And this is how Rescue Copter was born.

Rescue Copter is a side-scrolling-retro-style-mix-of-a-choplifter-and-firefighter game. It has pixel graphics and 8-bit sounds – everything I love in retro games.

Rescue Copter: To the rescue
Rescue Copter: To the rescue

Dealing with size

The most obvious challenge for this compo was the size. 13kB for a complete web game isn’t to much. You have to fit all the code, graphics and sounds in it.

Thankfully there are some tricks that could help you in that I’m posting below.

Use sprite sheets

May sound pretty obvious for some of you, but merging all the images into one could save you around 15% of the initial size.

For this purpose I used ShoeBox – a free and simple application combining multiple UI related tools. It can create a sprite sheet with a sprite atlas and is highly customizable.

Here’s a sprite sheet for the Rescue Copter:

Rescue Copter sprite sheet
Rescue Copter sprite sheet

Generate sounds

I came across JSFXR some time ago and I knew I’m gonna use it for my next project.

It’s a JavaScript port of as3sfxr – a simple yet powerful sound generation tool. Having 2,5kB minified it fits perfectly in js13k rules. Even though it might not match every kind of game, I encourage you to give it a go.

You can use online version of as3sfxr to create a sound and then store it in a simple array.

Here’s the sound of picking a coin I used:

D.R.Y. – let the builder do the job

I knew that sooner or later I’m gonna need a build tool which will cover all the repetitive tasks and save me a lot of time. For this purpose I used Grunt – a JavaScript task runner with a huge database of plugins.

My builder did the following:

  • validated the code
  • concatenated all the scripts into a single one
  • wrapped the code in a closure (for a better compression and a bit of security)
  • “minified” the script
  • replaced multiple script tags in the index.html file with a single one pointing to the concatenated, compressed code
  • injected CSS to the index.html file
  • compressed the HTML file
  • compressed and copied the sprite sheet
  • packed all the code into a ZIP file reporting the final size

Check out my gruntfile.js for more details.

Generate everything

Procedural content generation is a key. Generating levels and graphics could save you a lot of data. In my game, I used midpoint displacement algorithm to create the terrain. I also wrote a simple pseudorandom number generator to make the numbers generation reproducible.

Dealing with time

clock

The other challenge was time. Even though having a whole month to write a small game may sound easy, it’s really difficult to maintain focus and keep yourself motivated all the time.

There were few things that really helped me I advise you to try.

Have a plan

May sound obvious, but having a detailed plan of what you’ll be doing for the next month was really helpful. I had a multi-level list of task to do during next 30 days. The higher level on the list – the more general topics.

So at the top I had things like: gameplay, core, entities, graphics, sounds, builder etc. Then, on a lower level there were more detailed task like: sprite manager, graphics for entities, backgrounds etc. and on the lowest level most specific task e.g. 2-frame chopper animation, x background gradients, x ground tiling textures etc. This really helped me see where I am and how much work I have to complete the game.

My TODO list example:

  • graphics
    • sprite manager
    • entity graphics
      • copter animation (2 frames)
      • 3 background gradients
  • core
    • copter class
      • copter movement
      • copter collisions
    • game states
      • main menu
      • pause screen
      • gameplay
  • sounds
    • sound manager
    • sounds
      • copter
      • coin
      • collision
  • builder

Work every day

This is the hardest part – to sit in front of a computer …after 8 hours of work, where you were:

  • sitting on a computer…
  • …writing a code…
  • …in JavaScript

Yeah, sounds a bit depressing and can be really hard in the long run. I forced myself to do this everyday, just to write a single piece of code – no matter if it’s a seed-based pseudorandom number generator, Background class, procedural terrain generator or a collision detection.

There were days I could write a few hundred lines of code, but sometimes it was just ten…

So keep in mind, that every single line of code, every single pixel painted or a sound generated brings you closer to the goal.

Reduce the scope, then reduce it again

Whole month to write a game, huh? So let it have 48 levels in 6 different locations. Or 8. And 12 types of opponents, and… Stop. It won’t happen. Start small and then, if you see you’re progressing well, the gameplay feels good, the graphics are fine and you still have a lot of time and data, try to add something. It’s much easier to add new pieces to a running game than suddenly remove bits of code and images just few hours before the deadline. Been there – done that – won’t try again.

Watch how you progress to recharge your batteries

It’s cool to see how my project evolved. Each day I took a screenshot of my work. Then, when I was low, I opened the folder, browsed the images and could see how much progress I made from the beginning. This helped me to regain my motivation to work for another afternoon.

Cool things I’ve learned

Finally, a small list of cool things I’ve learned during this compo:

Midpoint displacement algorithm

This one is really cool and I’m gonna reuse it someday. In a few lines of code you can create endless number of terrains. Check out this excellent post by Loktar for more info.

Fixed timestep in the physics loop

Having a predictive physics engine is very important. The game could run on many different devices and the FPS rate may vary so it’s crucial to make the experience fair and similar.

For example people with faster PCs shouldn’t be able to jump higher or fly faster just because they have more FPS. That’s where a fixed timestep can help.

Here’s my game loop (removed some less important parts):

I’m buffering time deltas up to 1 second and then I execute the update loop with my predefined game step (1/60th of a second) until the buffer runs out. The remaining time in the delta buffer if passed to the next cycle.

The rendering loop is executed with RAF so it’s already optimized by a browser.

Pseudorandom number generator

There are tons of pseudorandom number generation algorithms on the web and I recommend you trying some of them. These are very useful when it comes to making things reproducible.

Crisp pixel graphics in <canvas>

I fought with that for a while. Every time I scaled an image, it was automatically anti-aliased and broke the overall effect. There were multiple ways to solve that problem, e.g. using image-rendering CSS rule, but none of them worked for me.

Finally, I used ctx.imageSmoothingEnabled = false with some vendor prefixes and that did the trick.

Image Smoothing - enabled on the left, disabled on the right
Image Smoothing – enabled on the left, disabled on the right

Summary

Well, this post came out much longer than I expected. I hope you found it useful in some way.

If you’re looking for more details on the code, feel free to fork my Rescue Copter Github repo. I’m going to add more code comments there soon.

Also, I encourage you to participate in the next edition of js13kGames or any other gamedev compo. Check out CompoHub to find any. It’s a great experience and could teach you a lot.

Finally, give some love to @end3r who drives js13kGames for 3 years now and check out this year’s entries – some of them are just mind-blowing!

Now I’m going back to kill more of those freakin’ Weasels… ^_^

by Greg

3 Comments »

3 thoughts on “js13kGames 2014 post mortem – I finally finished something!”

  • Nice post-mortem. Congrats on the 12th place.

    I too am happy I finished and submitted something, anything, on time. It was a lot harder to do than I had expected. I ended up with a bunch of half-begin prototypes that I didn’t like enough to spend any more time on, so I leapt onto another idea. Great that you had one from the beginning and worked through to the end.

    Thanks for posting your GruntFile. I need to look at the modules you use to replace things in the HTML. The idea of using a whole bunch of JS files to keep things cleaner never occurred to me, but makes perfect sense. I love how nice Grunt makes working with JS projects.

    BTW the link to Shoebox is missing the HTTP, so it in-linking in your site.

    • Thanks for pointing out this Shoebox link problem – it’s fixed now.

      The module replacing stuff in the HTML file is called grunt-htmlrefs and it’s great. Check out https://github.com/tactivos/grunt-htmlrefs for more info.

      And regarding source files – I used separate JS files to keep the code clean and kinda modular (I know these are not true modules but thinking this way helped me to maintain the code).

  • Congratulation on finishing your game and winning 12th place! I really like it. It reminds me ‘Air rescue’ a bit ;).

    Thanks for that article. I will read your blog more often :).

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

We are using Google Analytics

Please confirm, if you accept our Google Analytics tracking. You can also decline the tracking, so you can continue to visit our website without any data sent to Google Analytics.