JavaScript: Do you like games? (part 4)

Written by radu.bogdan.gaspar | Published 2016/11/06
Tech Story Tags: javascript | game-engine | blitting | canvas | es6

TLDRvia the TL;DR App

On this topic: Part 1 | Part 2 | Part 3 | Part 4 | Part 5 | Part 6

We’re going to continue with DisplayObject which extends Scene. You can review the schema in part 1 of this series but we’re going to make some changes to how things are named.

Its job is very simple; it will group objects that need to be rendered in a children array and it will expose methods which allow us to add and remove elements to this array. It’s actually very similar in functionality to the Canvas.

We want everything that’s being rendered in our Scene to be a DisplayObject. Imagine a game where we have a space ship. Whenever the ship moves, its thrusters are ignited. The DisplayObject could hold both the ship and the thrusters as they belong together.

Splitting the objects like this means we can also test for collision separately as something colliding with the thrusters should not register a hit on the ship.

You can use this logic to go even further and add the ability to collect power-ups that add more firepower to the ship and make those enhancements destructible by enemy fire, etc.

For now we only care about the grouping logic:

engine/ui/DisplayObject.js

Sprite sheets: what they are and what do they offer?

A game usually needs a lot of graphical assets. In the case of 2D games, these images are usually referred to as sprites. Every image has a width, height and color depth. Pixels are defined by 4 channels:

  • 3 color channels (red, green, blue) and
  • 1 alpha channel.

Every channel has a value from 0 to 255. You can store the maximum value of 255 using 8 bits (255 = 11111111 in binary); 4 channels * 8 bits per channel = 32 bits. That’s a total of 4 bytes for each pixel. The memory required for a 64x64 image is 64*64*4 = 16.3 kilobytes.

That’s not a lot, but managing multiple characters, items and animations can affect memory and implicitly performance. The best way is to store all graphical assets in a single sprite sheet. Here’s a partial sprite sheet example from the Angry Birds game:

© Rovio Entertainment — Angry Birds Sprite Sheet (868 x 898 px)

It has the various pigs, the damage stages they receive, as well as some of the birds. There are tools which help you create sprite sheets, like TexturePacker but you could also create your own utility that does this relatively easy.

You can create sprite sheets with fixed width and height cells or you can optimize them by cropping out the transparent parts making every sprite takes as little space as possible.

Our engine will have a SpriteSheet class which will implement a getTile() method to return a specific tile based on its position. We’ll currently work with the idea that all tiles in the sheet have the same width and height (so if one image is 64x32px, they all are).

engin/ui/SpriteSheet.js

It takes two parameters: a loaded image and the tile size. The loaded image will be provided by our AssetsLoader utility class which we covered in part 2 of this series.

Since our sprite sheets will have a fixed width and height for each tile, we can traverse the entire image and store the coordinates of each of the sprites.

Animated sprites are no different, take a look at this Ryu sprite sheet from the Street Fighter game:

Ryu Sprite Sheet (702 x 130 px)

It’s the same concept as movie reels. You have a lot of frames with static images which you traverse at a high frame rate. This gives the illusion of movement.

So what would an AnimatedSprite class look like? Well it should have a few things like:

  • the x and y position
  • the sprite sheet it’s working with
  • the start and end frame of the animation
  • whether the animation should loop forever (default true) — at a later time we could also allow for this to be a number (like loop 3 times)
  • if the loop is false it should stop on the last frame
  • the animation frame rate (unless we want to toggle through those frames 60 times per second)
  • in the image above Ryu has 9 frames but we’re working on a 0 based index, so the frames are counted left to right from 0 to 8; this will be the default approach in our class

engine/ui/AnimatedSprite.js

This is know as blitting. The frame rate control is managed with Date instances inside the update() method. We check if the time that’s elapsed is greater than the period… if it is, we simply increment the frame index by 1; both the update() and render() methods are still called ~60 times per second, this doesn’t affect us.

Let put everything together:

sprites/index.js

Here’s a working demo of Ryu animating on a loop at 10 fps:

Source code:

raduGaspar/game-physx_game-physx - A small utility for JavaScript game making_github.com

In the next article we’ll attempt creating a simple game like Asteroids or Space Invaders with the engine we have so far.


Published by HackerNoon on 2016/11/06