Skip to content

Instantly share code, notes, and snippets.

@David-Else
Created January 24, 2018 17:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save David-Else/707458147b99593458db57202807ef7a to your computer and use it in GitHub Desktop.
Save David-Else/707458147b99593458db57202807ef7a to your computer and use it in GitHub Desktop.
/* http://www.elsewebdevelopment.com/ */
(function () {
'use strict';
const utils = {
events: {},
// utils.subscribe("nameofevent", callback);
// utils.publish("nameofevent", 4);
// utils.unsubscribe("nameofevent", callback);
subscribe(eventName, fn) {
this.events[eventName] = this.events[eventName] || [];
this.events[eventName].push(fn);
},
unsubscribe(eventName, fn) {
if (this.events[eventName]) {
for (let i = 0; i < this.events[eventName].length; i += 1) {
if (this.events[eventName][i] === fn) {
this.events[eventName].splice(i, 1);
break;
}
}
}
},
publish(eventName, data) {
if (this.events[eventName]) {
this.events[eventName].forEach((fn) => {
fn(data);
});
}
},
randomNumber(upperNumberLimit) {
return Math.random() * upperNumberLimit;
},
randomBoolean() {
return Boolean(Math.floor(Math.random() * 2));
},
toRadians(angle) {
return (angle * Math.PI) / 180;
},
};
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let rightPressed;
let leftPressed;
let firePressed;
function keyHandler(event) {
// event.preventDefault();
switch (event.code) {
case 'KeyF':
if (event.type === 'keydown') {
firePressed = true;
} else {
firePressed = false;
}
break;
case 'ArrowLeft':
if (event.type === 'keydown') {
leftPressed = true;
} else {
leftPressed = false;
}
break;
case 'ArrowRight':
if (event.type === 'keydown') {
rightPressed = true;
} else {
rightPressed = false;
}
break;
// no default
}
}
addEventListener('keydown', keyHandler);
addEventListener('keyup', keyHandler);
function render({
onScreenObjects,
remainingZombieCount,
remainingBulletCount,
}) {
ctx.clearRect(0, 0, canvas.width, canvas.height); // clear the canvas
ctx.font = '25px Arial';
ctx.fillText(`Zombies ${remainingZombieCount}`, 10, 35);
ctx.fillText(`Bullets ${remainingBulletCount}`, 10, 70);
onScreenObjects.forEach((onScreenObject) => {
ctx.save();
ctx.translate(onScreenObject.xPosition, onScreenObject.yPosition);
ctx.rotate(utils.toRadians(onScreenObject.angle));
ctx.fillStyle = onScreenObject.color;
ctx.fillRect(
onScreenObject.width / -2, onScreenObject.height / -2,
onScreenObject.width, onScreenObject.height,
);
ctx.restore();
});
}
function createHeroObject({
xPosition,
yPosition,
}) {
return {
description: 'hero',
width: 25,
height: 50,
xPosition,
yPosition,
angle: 0,
color: 'red',
spin(direction) {
switch (direction) {
case 'left':
this.angle -= 1;
break;
case 'right':
this.angle += 1;
break;
// no default
}
},
};
}
function createZombieObject({
xPosition,
yPosition,
speed,
geneticStupidity,
zombieStupidityThreshold,
}) {
return {
description: 'zombie',
width: 15,
height: 15,
xPosition,
yPosition,
speed,
geneticStupidity,
zombieStupidityThreshold,
angle: 0,
color: 'green',
move(direction) {
const envrStupidity = utils.randomBoolean();
let zombieIsClever = false;
if (this.geneticStupidity && envrStupidity) {
zombieIsClever = true;
this.color = 'orange';
}
switch (direction) {
case 'up':
if (zombieIsClever) {
this.yPosition -= this.speed;
} else {
this.yPosition += this.speed;
}
break;
case 'down':
if (zombieIsClever) {
this.yPosition += this.speed;
} else {
this.yPosition -= this.speed;
}
break;
case 'left':
if (zombieIsClever) {
this.xPosition -= this.speed;
} else {
this.xPosition += this.speed;
}
break;
case 'right':
if (zombieIsClever) {
this.xPosition += this.speed;
} else {
this.xPosition -= this.speed;
}
break;
default:
throw new Error(`move() there is no direction called ${direction}`);
}
},
};
}
function createBulletObject({
xPosition,
yPosition,
angle,
}) {
return {
description: 'bullet',
width: 5,
height: 10,
xPosition,
yPosition,
angle,
color: 'black',
fly() {
this.xPosition += 1 * Math.sin(utils.toRadians(this.angle));
this.yPosition -= 1 * Math.cos(utils.toRadians(this.angle));
},
};
}
// for es6 modules in firefox turn on dom.module.scripts in about:config
let tempHeroXpos; // for the animate loop so zombies can know where to attack
let tempHeroYpos; // this can go, we can find a better solution!
let tempHeroAngle;
let tempHeroWidth;
let tempHeroHeight;
const onScreenObjects = [];
const numberOfZombies = 50;
const initialBulletCount = 500;
const zombieStupidityThreshold = 0.75;
const distanceFromHeroRequired = 3;
const maxZombieSpeed = 0.5;
let remainingZombieCount = numberOfZombies;
let remainingBulletCount = initialBulletCount;
const shootSound = new Audio('../assets/shoot.wav');
const deadZombieSound = new Audio('../assets/invaderkilled.wav');
const deadPlayerSound = new Audio('../assets/explosion.wav');
function createHero() {
onScreenObjects.push(createHeroObject({
xPosition: canvas.width / 2,
yPosition: canvas.height / 2,
}));
}
function createBullet({
fireAngle,
}) {
utils.publish('bulletFired', 'fire');
onScreenObjects.push(createBulletObject({
xPosition: tempHeroXpos,
yPosition: tempHeroYpos,
angle: fireAngle,
}));
remainingBulletCount -= 1;
}
function generateRandomZombiePosition(axis) {
// create random screen position, if greater than the middle on either axis push it away
function randomPosition(axisLength) {
const randomPos = utils.randomNumber(axisLength);
return (randomPos > axisLength / 2) ?
randomPos + (axisLength / distanceFromHeroRequired) :
randomPos - (axisLength / distanceFromHeroRequired);
}
// return random number for chosen axis for zombie position
return (axis === 'xAxis') ?
Math.round(randomPosition(canvas.width)) :
Math.round(randomPosition(canvas.height));
}
function createZombies() {
for (let i = 0; i < numberOfZombies; i += 1) {
onScreenObjects.push(createZombieObject({
xPosition: generateRandomZombiePosition('xAxis'),
yPosition: generateRandomZombiePosition('yAxis'),
speed: utils.randomNumber(maxZombieSpeed),
zombieStupidityThreshold,
geneticStupidity: utils.randomBoolean(),
}));
}
}
/**
* Act on the variables sent from the key handler in the view
*/
function actOnKeyPress() {
if (firePressed && remainingBulletCount > 0) {
createBullet({
fireAngle: tempHeroAngle,
});
}
}
/**
* Make things move on screen
*/
function animate() {
onScreenObjects.forEach((onScreenObject) => {
// Assign a temp variable to hero's x and y for zombie to detect, spin if button pressed
if (onScreenObject.description === 'hero') {
tempHeroXpos = onScreenObject.xPosition;
tempHeroYpos = onScreenObject.yPosition;
tempHeroAngle = onScreenObject.angle;
tempHeroWidth = onScreenObject.width;
tempHeroHeight = onScreenObject.height;
if (rightPressed) {
onScreenObject.spin('right');
} else if (leftPressed) {
onScreenObject.spin('left');
}
}
// Make the bullets fly in the direction they were fired
if (onScreenObject.description === 'bullet') {
onScreenObject.fly();
}
// Make each zombie attack the hero, one iteration at a time
if (onScreenObject.description === 'zombie') {
// if (onScreenObject.geneticStupidity < zombieStupidityThreshold) {
// zombieIsClever = true;
// }
const zombieIsClever = false;
// If zombie is right of hero
if (tempHeroXpos < onScreenObject.xPosition) {
if (zombieIsClever) {
onScreenObject.move('left');
} else {
onScreenObject.move('right');
}
}
// If zombie is left of hero
if (tempHeroXpos > onScreenObject.xPosition) {
if (zombieIsClever) {
onScreenObject.move('right');
} else {
onScreenObject.move('left');
}
}
// If zombie is below hero
if (tempHeroYpos < onScreenObject.yPosition) {
if (zombieIsClever) {
onScreenObject.move('up');
} else {
onScreenObject.move('down');
}
}
// If zombie is above hero
if (tempHeroYpos > onScreenObject.yPosition) {
if (zombieIsClever) {
onScreenObject.move('down');
} else {
onScreenObject.move('up');
}
}
}
});
}
/**
* Detect on-screen collisions
*/
function detectCollision() {
const edgeOfScreen = 0;
// Check every zombie
onScreenObjects.forEach((zombie, zombieIndex) => {
const zombieCollidesWithHero =
zombie.xPosition < tempHeroXpos + tempHeroWidth &&
zombie.xPosition + zombie.width > tempHeroXpos &&
zombie.yPosition < tempHeroYpos + tempHeroHeight &&
zombie.height + zombie.yPosition > tempHeroYpos;
if (zombie.description === 'zombie' && zombieCollidesWithHero) {
utils.publish('playerDead', 'deadPlayer');
throw new Error('You died!');
}
if (zombie.description === 'zombie') {
// Check every bullet while iterating through each zombie
onScreenObjects.forEach((bullet, bulletIndex) => {
const bulletHitsScreenBoundary =
bullet.yPosition < edgeOfScreen ||
bullet.yPosition > (canvas.height - edgeOfScreen) ||
bullet.xPosition < edgeOfScreen ||
bullet.xPosition > (canvas.width - edgeOfScreen);
const bulletHitsZombie =
zombie.xPosition < bullet.xPosition + bullet.width &&
zombie.xPosition + zombie.width > bullet.xPosition &&
zombie.yPosition < bullet.yPosition + bullet.height &&
zombie.height + zombie.yPosition > bullet.yPosition;
if (bullet.description === 'bullet' && bulletHitsScreenBoundary) {
onScreenObjects.splice(bulletIndex, 1);
}
if (bullet.description === 'bullet' && bulletHitsZombie) {
onScreenObjects.splice(zombieIndex, 1);
onScreenObjects.splice(bulletIndex, 1);
remainingZombieCount -= 1;
utils.publish('zombieDead', 'zombieDead');
}
});
}
});
}
/**
* Play a sound when pub-sub sends a signal to do so
*/
function playSound(data) {
switch (data) {
case 'fire':
shootSound.play();
break;
case 'zombieDead':
deadZombieSound.play();
break;
case 'deadPlayer':
deadPlayerSound.play();
break;
default:
throw new Error(`playSound() there is no sound called ${data}`);
}
}
/**
* The main loop that runs repeatedly
*/
function mainLoop() {
render({
onScreenObjects,
remainingZombieCount,
remainingBulletCount,
});
actOnKeyPress();
animate();
detectCollision();
requestAnimationFrame(mainLoop);
}
/**
* INIT in an IIFE
*/
(function init() {
createZombies();
createHero();
utils.subscribe('bulletFired', playSound);
utils.subscribe('zombieDead', playSound);
utils.subscribe('playerDead', playSound);
mainLoop();
}());
}());
//# sourceMappingURL=bundle.js.map
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment