Skip to content

Javascript one-page game with elements of cannon shooter, pool, shuffleboard, and asteroids

Notifications You must be signed in to change notification settings

taisiat/bouncy-bee

Repository files navigation

bouncy-bee

Javascript game akin to cannon shooter, pool, Asteroids, and shuffleboard

Background

BouncyBee is a single-page game where cannon shooter meets pool and shuffleboard and Asteroids. The player launches a bee from a static spot, and the bee then bounces around the gameboard before decelerating to a stop. While the bee flies, the player has a chance to nudge its trajectory to collect points and avoid enemies.

The player earns points when the bee flies across (pollinates!) flowers, and gets extra points if the bee lands back at the beehive. The player loses if the bee depletes its health fully, which happens when it runs into the wasps that move across the gameboard.

Play it 🐝 here!
Or, watch the quick ▶️ video walkthrough.

Github
Linkedin
Portfolio

Functionality & MVPs

In BouncyBee, users are able to:

  • Set initial bee direction and speed, akin to cannon shooter
  • Nudge the bee's flight trajectory as it bounces and accelerates/decelerates around the game area, akin to asteroids and pool
  • 'Power up' bee's speed and experience deceleration, akin to shuffleboard
  • Start, pause, and reset the game, while maintaining the browser session's high score
  • Experience delightful visual feedback based on the bee's position, speed, and direction

Technologies, Libraries, APIs

This project is implemented with the following technologies:

  • The Canvas API to render the game board, with requestAnimationFrame and time delta correction behind the scenes to correct for play speed across devices
  • Webpack and Babel to bundle and transpile the source JavaScript code
  • npm to manage project dependencies
  • CSS and HTML to generate the webpage around the game play area and the welcome screen
  • Keymaster to bind keys to methods

Implementation Highlights

Pollen Sparkles

When the player flies the bee over flowers, pollen sparkles appear:

Pollen sparkles

Pollen sparkles are extra delightful because their color and position is randomized, and they have a few frames of persistence, making them slightly lag in the bee's pollination wake.

Pollen sparkles are implemented as a Pollen class, with each sparkle being an instance. Bee <> flower collision detection in the Game class activates new pollen sparkle creation.

When each pollen sparkle is created, its position is set with a call to Pollen.pollenPos(), which leverages a math helper method in a math utilities file:

pollen.js
  pollenPos() {
    let posCorrection = 0;
    return Util.randomPosAroundCenterpoint(
      this.game.bee.pos,
      CONSTANTS.POLLEN_DIST,
      posCorrection
    );
  }

The math helper function generates a random position radially around the given centerpoint using Math.cos() and Math.sin(), rather than 'squarely' using Math.random() on the centerpoint directly. This enables pollen sparkles to appear in a pleasant circular area around the bee.

util.js
export function randomPosAroundCenterpoint(centerpoint, distance, correction) {
  let posRadius = distance * Math.sqrt(Math.random());
  let angleInRad = Math.random() * 2 * Math.PI;
  return [
    centerpoint[0] + posRadius * Math.cos(angleInRad) + correction,
    centerpoint[1] + posRadius * Math.sin(angleInRad) + correction,
  ];
}

The Pollen class sets pollen sparkle color by picking a random HEX code from an array during instance construction.

A math helper, Util.generateHexagonPoints(this.pollenPosition,Pollen.RADIUS), is called during instance construction to generate the hexagon point coordinates (points) that shape the pollen sparkle (this is a bee themed game after all!).

The Pollen class then draws pollen in 2 steps. 1: check that the pollen sparkle is 'younger' than a certain number of frames, else delete the pollen sparkle instance. 2: Draw the hexagon as a Canvas API path shape using the pre-determined points and color.

pollen.js
drawPollen(ctx) {
    if (this.pollenTimer > Pollen.PERSISTENCE) {
      this.game.remove(this);
    }

    ctx.fillStyle = this.color;
    ctx.beginPath();
    this.points.forEach((pos) => {
      ctx.lineTo(pos[0], pos[1]);
    });
    ctx.closePath();
    ctx.fill();

    this.pollenTimer += 1;
  }

Cannon Shoot Mechanic

The game begins with the bee at the beehive, and the player must set a direction and launch speed - like in cannon shooters. Direction adjusts based on the player's keyboard inputs, and speed slides on its own and is 'locked in' based on player's timing of bee launch:

Cannon shoot stage

To set direction, the GameView class listens to inputs from ASDW and arrow keys. It also checks if the bee is launched and which direction it is traveling in, and fires off different methods conditionally:

game_view.js, W handler example
  wKeyHandler() {
    let nudgeDirection = {
      up: "",
      down: "",
      left: "right",
      right: "left",
    };
    let beeDirection = this.game.bee.beeDirection();

    if (this.game.bee.launched) {
      this.game.bee.nudge(nudgeDirection[beeDirection]);
    } else if (!this.game.bee.launched) {
      this.game.bee.setTrajectory(this.directions.UP);
    }
  }

If the bee is not yet launched, then Bee.setTrajectory(direction) is activated, and nudges Bee.START_VEL. This velocity is a placeholder whose value overrides bee's vel once the bee is set in motion. Bee's vel is what dictates actual bee position during gameplay.

bee.js
  setTrajectory(direction) {
    let nudgeFactor = 0;
    if (direction === this.directions.UP) {
      nudgeFactor = CONSTANTS.NUDGE;
    } else if (direction === this.directions.DOWN) {
      nudgeFactor = -CONSTANTS.NUDGE;
    }
    const cosA = Math.cos(nudgeFactor);
    const sinA = Math.sin(nudgeFactor);
    const newX = Bee.START_VEL[0] * cosA + Bee.START_VEL[1] * sinA;
    const newY = -Bee.START_VEL[0] * sinA + Bee.START_VEL[1] * cosA;
    Bee.START_VEL = [newX, newY];
  }

To draw the arrow representing the selected direction, Bee.drawTrajectory(ctx) extrapolates the arrow tip from the current Bee position and the nudged Bee.START_VEL value, and calls the math helper Util.calculateTriangleCoord(this.pos, pointerDirection) to calculate where the 2 other triangle points are. This helper does this using the angle between the bee position and the arrow tip. This ensures the arrow moves in a circle and always points outward!

util.js
export function calculateTriangleCoord(originPos, pointerTipPos) {
  const xDistance = Math.abs(originPos[0] - pointerTipPos[0]);
  const yDistance = Math.abs(originPos[1] - pointerTipPos[1]);
  const pointDistance = Math.sqrt(xDistance ** 2 + yDistance ** 2);
  let knownPointsAngleDeg = (Math.atan(yDistance / xDistance) * 180) / Math.PI;
  let inverseAngleDeg = 90 - knownPointsAngleDeg;
  let desiredSharpnessDeg = 30;
  let pointerLength = pointDistance * 0.2;
  let newPoints = [];
  [1, -1].forEach((factor) => {
    let y =
      pointerLength *
      Math.cos(
        ((inverseAngleDeg + factor * desiredSharpnessDeg) * Math.PI) / 180
      );
    let x =
      pointerLength *
      Math.sin(
        ((inverseAngleDeg + factor * desiredSharpnessDeg) * Math.PI) / 180
      );
    let xAdj = originPos[0] <= pointerTipPos[0] ? x : -x;
    let yAdj = originPos[1] <= pointerTipPos[1] ? -y : y;
    newPoints.push([pointerTipPos[0] - xAdj, pointerTipPos[1] + yAdj]);
  });
  return newPoints;
}

Meanwhile the speed setter, Bee.slideScale(), increments slideStep up or down with each game step and updates the Bee's speed value by that factor. The slider is then drawn as a rectangle of variable width by Bee.drawScale(ctx), based on the current speed value.

When the player activates launch with the space bar, the current game step's Bee speed and Bee.START_VEL values are locked in. A math helper method calculates the final velocity, and the Bee vel is updated to the resultant velocity value. Bee launches!

bee.js
  launch() {
    this.vel = Util.scale(Bee.START_VEL, this.speed);
    this.launched = true;
  }

Each time the game steps forward, the Game class checks if the bee is launched or not, and stops drawing the slider and arrow if the bee is no longer in cannon launch mode. This makes for a cleaner interface that shows these visuals only when they are relevant.

Other Features

Take a look at the source files for implementation of other notable features:

  • Bee acceleration and deceleration mechanics, including how bee speed triggers beehive sparkles and how top speed is capped. See Bee class.
  • requestAnimationFrame with time deltas for consistent game speed. See GameView and MovingObject game classes.
  • Gameboard setup with randomized yet fenced positions for wasps, flowers, and speed strips. Note that flowers and speed strips spawn at positions that never overlap each other. See Game class.
  • Bounce mechanics. See util.js.
  • Bee and wasp sprite sheet parsing, both with traditional one-image sprite sheets and multi-file sprite frames. See Bee and Wasp classes.
  • Customized win/lose screens with changing messages based on game outcomes. See GameView class.

Future Features

Upcoming improvements include:

  • Add additional levels with different assortments of enemies and point-giving objects
  • Add additional areas of altered physics, such as wormholes

Asset Attribution