/aA-SnowBrawl

Primary LanguageJavaScript

SnowBrawl!

Introduction

SnowBrawl is a fun and addicting original platform game. SnowBrawl was built using only vanilla JavaScript and HTML Canvas.

Check out the live site here.

Gameplay

SnowBrawl is a game that gets more and more intense the longer you play. Snowballs are flying in from either direction, control the Bearling to dodge the snowballs by jumping over and ducking under them. If you get hit, it's game over!

My personal high score is 20, try to beat it!

Functionality

Players are able to:

  • Close the instructions modal with the 'Enter' key.
  • Move, jump, and duck using the 'W', 'S', 'D', and 'Space' keys.
  • Keep track of their score.
  • Restart the game with the 'Enter' key after losing.

Technologies Used

  • Vanilla JavaScript
  • HTML 5
  • CSS 3

Features

Canvas

The file structure used in SnowBrawl is very modular, objects and actions are organized into classes in order to separate concerns. Classes such as the player and snowballs are animated separately and manipulated within the main Game.js file, where the interactions are defined.

For instance, the way that the player moves is defined within the class, where boundaries and constraints are set in order to prevent bugs and exploits. Within the same function, I iterate over the character's spritesheet to animate them.

 move() {
    this.char.moving = false;
    if (this.keys['a'] && this.char.x > 160) {
      if (this.char.frameCount < 10) {
        this.char.frameCount++;
      } else if (this.char.spriteSheetY >= 17) {
        this.char.spriteSheetY--;
        this.char.frameCount = 0;
      } else {
        this.char.spriteSheetY = 17;
        this.char.frameCount = 0;
      }

      this.char.x -= this.char.speed;
      this.char.moving = true;
    }
    if (this.keys['d'] && this.char.x < 690) {
      if (this.char.frameCount < 10) {
        this.char.frameCount++;
      } else if (this.char.spriteSheetY <= 10) {
        this.char.spriteSheetY++;
        this.char.frameCount = 0;
      } else {
        this.char.spriteSheetY = 10;
        this.char.frameCount = 0;
      }
      this.char.x += this.char.speed;
      this.char.moving = true;
    }

    if (this.keys[' '] && !this.char.jumping) {
      let jump = new Audio('./src/assets/jump2.wav');
      jump.volume = 0.2;
      jump.play();
      this.char.y -= this.char.jumpStrength;
      this.char.jumping = true;
      this.char.moving = true;
      delete this.keys[' '];
    }
    if (!this.keys[' '] && this.char.y < 245) {
      this.char.y += this.char.gravity;
      this.char.moving = true;
      this.char.jumping = true;
    }

    if (this.keys['s'] && this.char.spriteSheetY < 14) {
      this.char.spriteSheetY = 13;
      this.char.moving = true;
      this.char.y = 252;
    }
    if (this.keys['s'] && this.char.spriteSheetY > 13) {
      this.char.spriteSheetY = 14;
      this.char.moving = true;
      this.char.y = 252;
    }
  }

  idleAnimationLogic() {
    if (this.char.spriteSheetY < 14 && this.char.moving === false) {
      if (this.char.frameCount < 10) {
        this.char.frameCount++;
      } else if (this.char.spriteSheetY < 8) {
        this.char.spriteSheetY++;
        this.char.frameCount = 0;
      } else {
        this.char.spriteSheetY = 0;
        this.char.frameCount = 0;
      }
      this.char.y = 245;
    }
    if (this.char.spriteSheetY > 13 && this.char.moving === false) {
      if (this.char.frameCount < 10) {
        this.char.frameCount++;
      } else if (this.char.spriteSheetY < 27) {
        this.char.spriteSheetY++;
        this.char.frameCount = 0;
      } else {
        this.char.spriteSheetY = 19;
        this.char.frameCount = 0;
      }
      this.char.y = 245;
    }
  }

Within the game file, snowballs are created and added to an array. From there, each snowball object is manipulated and checked for either a collision, or when it leaves the canvas (either adding to the score, or setting the game to be over, respectively)

 createSnowball() {
    if (this.spawnSnowball % 100 === 0) {
      this.difficulty++;
      this.snowballArray.push(
        new Snowball(this.ctx, this.difficulty + 0.1 * (Math.random() + 1))
      );
    }
    for (let i = 0; i < this.snowballArray.length; i++) {
      this.snowballArray[i].animate();
      if (this.utilities.detectCollision(this.player, this.snowballArray[i])) {
        this.gameOver = true;
      }
      if (
        this.snowballArray[i].ball.x > 960 &&
        this.snowballArray[i].initialX === 0
      ) {
        this.snowballArray.splice(i, 1);
        this.score++;
      } else if (
        this.snowballArray[i].ball.x < 0 &&
        this.snowballArray[i].initialX === 960
      ) {
        this.snowballArray.splice(i, 1);
        this.score++;
      }
    }
  }

To add some ambience, there's another canvas overlaying the actual game in order to emulate snowfall. An array of snowflakes is created, and each snowflake receives coordinates and velocity. Once they cross the lower boundary, their vertical coordinates reset to a random position to appear as a never-ending snowfall.

function addToSnowflakes() {
  for (let i = 0; i < amountSnowflakes; i++) {
    snowFlakes.push({
      // add n snowflakes as objects with properties
      x: Math.random() * snowCanvas.width, //random x coordinate based on canvas width
      y: Math.random() * snowCanvas.height, //random y coordinate based on canvas height
      opacity: Math.random(), //random opacity between 0 and 1
      xVelocity: random(-10, 10), //random x velocity using previously defined function
      yVelocity: random(-10, 10), //random y velocity using previously defined function
      radius: random(1, 5), //random radius of snowflake
    });
  }
}

Coming Soon

  • More randomized snowball trajectories
  • Power ups
  • Difficulty Settings