Making a Game with JavaScript
Jess
Posted on May 5, 2020
(Note: I'm moving my posts from my time at Flatiron School from my Github to this platform. This blog entry was first posted on March 18, 2020)
For my JavaScript/Rails Single Page Application (SPA) project I made a game called Invasion!, about my dog dreaming of battling squirrels in space. The game was made with JavaScript, HTML, and CSS and a backend Rails API to store and fetch player’s names and scores.
For the most part I utilized object orientated design. All of the game objects and sprites (images) are broken into classes. For example, the player, enemies, and bullets are all objects that inherit from GameObject
. Each GameObject
has update()
and draw()
methods. Anything pertaining to displaying sprites or text goes in draw
, and anything that manipulates these things goes into update
.
Example of game objects inheriting from a GameObject class:
class GameObject {
static all = [];
constructor() {
GameObject.all.push(this);
}
update() {
this.checkForCollision();
}
draw(ctx) {
const { sourceX, sourceY, sourceWidth, sourceHeight, x, y, width, height, image } = this.spriteObj;
ctx.drawImage(image, sourceX, sourceY, sourceWidth, sourceHeight, x, y, width, height);
}
// other methods to check for and handle
// collisions, out of bounds, etc ...
}
class Player extends GameObject {
constructor() {
super();
// other properties initialized here
}
update() {
super.update();
if (this.collided) {
ExplosionObject.createExplosion(this);
}
this.move();
// etc...
}
// no need for a draw method since nothing changes from
// the GameObject class
}
Upon initialization, each GameObject
is stored in a static variable array called all
. This way I was able to handle looping through updates and draws for every existing object at once.
class Game {
// constructor, other methods, etc...
update() {
// spawn enemies...
GameObject.all.forEach(obj => obj.update());
if (this.player.isHit) this.gameOver();
}
draw() {
this.ctx.clearRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
if (this.mode === "play") {
GameObject.all.forEach(obj => obj.draw(this.ctx));
}
}
}
Sprites were separated into their own classes, depending on whether they were animated or not. My regular sprite class, SpriteObject
consisted simply of a constructor that took in the source location on the spritesheet and size for the sprite, the (x,y) location and sizes I wanted, and created a new Image()
. The AnimatedSpriteObject
, which inherits from SpriteObject
, is a lot more complicated. Aside from the properties already mentioned, I needed to know how many rows, columns, and frames an animation had.
A sprite object does not inherit from GameObject
because the thing the sprite is an image/animation of does. For example, if an enemy squirrel ship appears on screen, a new enemy()
is created (which inherits from GameObject
. When it is created a new SpriteObject()
is created for the enemy and stored as this.spriteObj
on the enemy instance.
class Enemy extends GameObject {
constructor(spawnX, speed = 1) {
super();
this.spriteObj = new SpriteObject(Enemy.initObj(spawnX));
this.speed = speed;
}
}
static initObj(spawnX) {
return {
sourceX: 0,
sourceY: 176,
sourceWidth: 218,
sourceHeight: 169,
x: spawnX,
y: -170,
width: 218 / 2,
height: 169 / 2
}
}
Oh, I should mention that I used requestAnimationFrame
to handle the game looping. requestAnimationFrame
updates the browser roughly 60 times a second. It works similarly to setInterval
but performs better for game purposes.
In order to animate, I had to create a delay value and keep track of how many 'ticks' went by. Each 'tick' is a frame per second (fps). If I didn't use a delay then the images would pretty much loop at rapid speed and you would never accurately see the animation. I set my delay to 3; this way it would only update to the next image every 3fps. Then I reset the tickCount to 0 to start over for the next frame.
Animating the sprites turned out to be the most challenging part of this whole project. I spent a lot of time googling and watching YouTube videos before I could get it working properly. If you're interested in knowing more about game development using JavaScript I found this channel to be pretty helpful: PothOnProgramming.
If you'd like to check out Invasion! you can do so here: github
Posted on May 5, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.