DEV Community

Jess
Jess

Posted on • Edited on

Making a Game with JavaScript

(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 ...
}
Enter fullscreen mode Exit fullscreen mode
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
}
Enter fullscreen mode Exit fullscreen mode

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));
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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
    }
  }
Enter fullscreen mode Exit fullscreen mode

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

Top comments (6)

Collapse
 
indecisiverease profile image
Rease Kirchner

What a cute game! The screenshot showing Penny and the squirrels in spaceships make me smile. Have you submitted this game to any app stores?

Collapse
 
robotspacefish profile image
Jess

Thank you! It's just something small for my Flatiron School project, but I should definitely consider fleshing it out into something more. :)

Collapse
 
indecisiverease profile image
Rease Kirchner

I think it would be great as a web app! Maybe try working through the KaiOS developer portal to figure out how to adapt it. I've interviewed a few KaiOS app developers and several have had a great experience working with KaiOS and releasing simple but fun games. You'd have a better shot of getting some user engagement if you released in their KaiStore versus the competitive native app stores.

If you want to check it out: developer.kaiostech.com/

Thread Thread
 
robotspacefish profile image
Jess

cool thanks, I'll check it out!

Collapse
 
abodmicheal profile image
Abod Micheal (he/him)

nice

Collapse
 
robotspacefish profile image
Jess

Thank you!