Metapod Madness is a 4-player minigame built using HTML5 Canvas and JavaScript.
Metapod Madness is a 4-player minigame drawing inspiration from the N64 classic, Pokemon Stadium, remastered in the form of a web application. Avoid damage from the boulders being launched at your cuddly cocoon pokemon by using its only move, Harden. However, be careful about overusing Harden since it will gradually drain your HP. Compete with your friends to see whose coordination is the fittest... last one standing wins!
The animate(time)
method in the GameView
class minimizes graphic rendering lag through usage of HTML5 Canvas and recursively looping the requestAnimationFrame
method until the game is complete.
animate(time) {
const timeDelta = time - this.lastTime;
this.game.step(timeDelta);
this.game.draw(this.ctx);
this.lastTime = time;
this.gameOver = this.game.over;
if (!this.paused) {
requestAnimationFrame(this.animate);
if (this.gameOver) {
this.paused = true;
setTimeout(() => this.end(), 1000);
};
};
};
bindKeyHandlers () {
const keys = this.keys;
const keyDownHandler = (e) => {
for (let i = 0; i < 4; i++) {
if (e.key == keys[i]) {
this.game.metapodsHardened[i] = true;
};
};
};
const keyUpHandler = (e) => {
for (let i = 0; i < 4; i++) {
if (e.key == keys[i]) {
this.game.metapodsHardened[i] = false;
};
};
};
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);
};
Though I initially planned to utilize three.js
for this project, using diagonal lines as diminishing scale, I was able to reconfigure my Boulder
and Shadow
classes to craft the illusion of depth.
With each iteration of boulders that are added to the game, the shadows follow fixed vectors that uniformly spawn below their respective boulders, traverse diagonally toward their respective Metapods, and reach their final destination as the boulders fall to collide with the Metapods.
const shadowVelocities = [
[-0.62, 1.3],
[-0.28, 1.3],
[0.12, 1.3],
[0.48, 1.3],
];
The boulders spawn at various points closer to the vertical center of the canvas. They travel parallel to each other upwards, and upon reaching a fixed height, they grow in size and horizontally adjust to different points directly above their respective Metapod character models.
move(timeDelta) {
if (this.pos[1] < -500) {
this.width *= 1.5;
this.height *= 1.5;
this.vel = [0, 16];
switch (this.idx) {
case 0:
this.pos[0] = this.game.dimensionX * (1 / 8) - 60
break;
case 1:
this.pos[0] = this.game.dimensionX * (3 / 8) - 60
break;
case 2:
this.pos[0] = this.game.dimensionX * (5 / 8) - 60
break;
case 3:
this.pos[0] = this.game.dimensionX * (7 / 8) - 60
break;
default:
break;
};
};
super.move(timeDelta)
};
isCollidedWith(otherObject) {
const pos1 = this.pos;
const pos2 = otherObject.pos;
const centerDist = Math.sqrt(
Math.pow(pos1[0] - pos2[0], 2) + Math.pow(pos1[1] - pos2[1], 2)
);
const distance = (this.width / 2 + otherObject.width / 2);
return (centerDist < distance + 5 && centerDist > distance - 5);
};
An issue that I came across with using HTML5 Canvas was layering my Canvas element with the game's menu and victory screens. To solve this, I weaved a state attribute into my GameView
constructor function, initializing state accordingly: this.state = "menu";
Each state is tied to different absolutely positioned z-index planes which allow for a more holistic gaming experience.
Upon clicking "Start Game" on the menu screen, this.state = "game";
which introduces the main Canvas element displaying all the Metapods and launching boulders. Once at least 3 Metapods have fainted, this.state = "victory";
which then transitions the game into the victory screen.
Some ideas that I would love to implement in time:
If you have any feedback for patches or improvement, please feel free to share!