Last active
June 20, 2018 13:50
-
-
Save David-Else/33f5bddc1bc2d0f1f8a4a9c104b820a2 to your computer and use it in GitHub Desktop.
es6-zombie-attack-bundle
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* https://www.elsewebdevelopment.com/ */ | |
class OnScreenObject { | |
constructor({ xPosition, yPosition }) { | |
this.xPosition = xPosition; | |
this.yPosition = yPosition; | |
} | |
move() { | |
this.xPosition += this.xSpeed; | |
this.yPosition += this.ySpeed; | |
} | |
rotate(degreesToRotate) { | |
this.angle += degreesToRotate; | |
} | |
draw(ctx) { | |
ctx.save(); | |
ctx.beginPath(); | |
ctx.fillStyle = this.color; | |
ctx.translate( | |
this.xPosition + this.width / 2, | |
this.yPosition + this.height / 2, | |
); | |
if (this.angle !== null) { | |
ctx.rotate(this.angle * (Math.PI / 180)); | |
} | |
ctx.fillRect(this.width / -2, this.height / -2, this.width, this.height); | |
ctx.restore(); | |
} | |
detectCollision(other) { | |
const left = this.xPosition; | |
const right = this.xPosition + this.width; | |
const top = this.yPosition; | |
const bottom = this.yPosition + this.height; | |
const otherLeft = other.xPosition; | |
const otherRight = other.xPosition + other.width; | |
const otherTop = other.yPosition; | |
const otherBottom = other.yPosition + other.height; | |
return !( | |
left > otherRight || | |
right <= otherLeft || | |
top >= otherBottom || | |
bottom <= otherTop | |
); | |
} | |
} | |
class Hero extends OnScreenObject { | |
constructor({ xPosition, yPosition }) { | |
super({ xPosition, yPosition }); | |
this.angle = 0; | |
this.speed = 1; | |
this.color = 'red'; | |
this.width = 25; | |
this.height = 50; | |
this.alive = true; | |
this.firePaused = false; | |
} | |
} | |
class Zombie extends OnScreenObject { | |
constructor({ xPosition, yPosition }) { | |
super({ xPosition, yPosition }); | |
this.width = 15; | |
this.height = 15; | |
this.color = 'green'; | |
this.walkingSpeed = 0.1; | |
this.xSpeed = null; | |
this.ySpeed = null; | |
} | |
directTowardsHero(hero) { | |
if (hero.xPosition > this.xPosition) { | |
this.xSpeed = this.walkingSpeed; | |
} else { | |
this.xSpeed = -this.walkingSpeed; | |
} | |
if (hero.yPosition > this.yPosition) { | |
this.ySpeed = this.walkingSpeed; | |
} else { | |
this.ySpeed = -this.walkingSpeed; | |
} | |
} | |
} | |
class Bullet extends OnScreenObject { | |
constructor({ xPosition, yPosition, angle }) { | |
super({ xPosition, yPosition }); | |
this.angle = angle; | |
this.xSpeed = 1; | |
this.ySpeed = 1; | |
this.width = 5; | |
this.height = 15; | |
this.color = 'gray'; | |
} | |
move() { | |
this.xPosition += this.xSpeed * Math.sin(this.angle * (Math.PI / 180)); | |
this.yPosition -= this.ySpeed * Math.cos(this.angle * (Math.PI / 180)); | |
} | |
} | |
class Text extends OnScreenObject { | |
constructor({ xPosition, yPosition }) { | |
super({ xPosition, yPosition }); | |
} | |
// overwrite inherited method from onScreenObject | |
draw(ctx, numberOfZombies, numberOfBullets, lives) { | |
ctx.font = '25px Arial'; | |
ctx.fillText(`Zombies ${numberOfZombies}`, this.xPosition, this.yPosition); | |
ctx.fillText( | |
`Bullets ${numberOfBullets}`, | |
this.xPosition, | |
this.xPosition + 50, | |
); | |
ctx.fillText(`Lives ${lives}`, this.xPosition, this.xPosition + 75); | |
} | |
} | |
class Boundaries extends OnScreenObject { | |
constructor({ xPosition, yPosition, width, height }) { | |
super({ xPosition, yPosition, width, height }); | |
} | |
} | |
class Game { | |
constructor(canvas) { | |
this.canvasWidth = canvas.width; | |
this.canvasHeight = canvas.height; | |
this.ctx = canvas.getContext('2d'); | |
this.options = { | |
lives: 3, | |
numberOfBullets: null, | |
numberOfZombies: null, | |
}; | |
this.hero = new Hero({ | |
xPosition: this.canvasWidth / 2, | |
yPosition: this.canvasHeight / 2, | |
}); | |
this.text = new Text({ | |
xPosition: 10, | |
yPosition: 35, | |
}); | |
this.boundaries = new Boundaries({ | |
xPosition: 0, | |
yPosition: 0, | |
width: this.canvasWidth, | |
height: this.canvasHeight, | |
}); | |
this.sounds = { | |
playerShoots: new Audio('./assets/shoot.wav'), | |
zombieDies: new Audio('./assets/invaderkilled.wav'), | |
playerDies: new Audio('./assets/explosion.wav'), | |
}; | |
this.zombies = []; | |
this.bullets = []; | |
} | |
createZombies() { | |
function randomOnScreenPosition(axisLength, heroPosition) { | |
const distanceFromHeroRequired = 10; | |
const randomPos = Math.round(Math.random() * axisLength); | |
return randomPos > heroPosition | |
? randomPos + axisLength / distanceFromHeroRequired | |
: randomPos - axisLength / distanceFromHeroRequired; | |
} | |
for (let i = 0; i < this.options.numberOfZombies; i += 1) { | |
this.zombies.push( | |
new Zombie({ | |
xPosition: randomOnScreenPosition( | |
this.canvasWidth, | |
this.hero.xPosition, | |
), | |
yPosition: randomOnScreenPosition( | |
this.canvasHeight, | |
this.hero.yPosition, | |
), | |
}), | |
); | |
} | |
} | |
createBullet() { | |
this.bullets.push( | |
new Bullet({ | |
xPosition: this.hero.xPosition + this.hero.width / 2, | |
yPosition: this.hero.yPosition + this.hero.height / 2, | |
angle: this.hero.angle, | |
}), | |
); | |
} | |
deleteZombie(zombieIndex) { | |
this.zombies.splice(zombieIndex, 1); | |
this.options.numberOfZombies -= 1; | |
} | |
deleteBullet(bulletIndex) { | |
this.bullets.splice(bulletIndex, 1); | |
} | |
detectCollision() { | |
// outer loop for zombies | |
this.zombies.forEach((zombie, index) => { | |
if (this.hero.detectCollision(zombie)) { | |
this.hero.alive = false; | |
return; | |
} | |
// inner loop for bullets | |
this.bullets.forEach((bullet, innerIndex) => { | |
if (zombie.detectCollision(bullet)) { | |
this.deleteZombie(index); | |
this.deleteBullet(innerIndex); | |
} else if (!this.boundaries.detectCollision(bullet)) { | |
this.deleteBullet(innerIndex); | |
} | |
}); | |
}); | |
} | |
// make all zombies go towards the hero | |
animateZombies() { | |
this.zombies.forEach(zombieInArray => { | |
zombieInArray.directTowardsHero(this.hero); | |
zombieInArray.move(); | |
zombieInArray.draw(this.ctx); | |
}); | |
} | |
// make all bullets on the screen continue firing in their path | |
animateBullets() { | |
this.bullets.forEach(bulletInArray => { | |
bulletInArray.move(); | |
bulletInArray.draw(this.ctx); | |
}); | |
} | |
clearCanvas() { | |
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight); | |
} | |
} | |
// keyboard handler | |
let rightPressed; | |
let leftPressed; | |
let firePressed; | |
function keyHandler(event) { | |
switch (event.code) { | |
case 'KeyF': | |
firePressed = event.type === 'keydown'; | |
break; | |
case 'ArrowLeft': | |
event.preventDefault(); | |
leftPressed = event.type === 'keydown'; | |
break; | |
case 'ArrowRight': | |
event.preventDefault(); | |
rightPressed = event.type === 'keydown'; | |
break; | |
// no default | |
} | |
} | |
addEventListener('keydown', keyHandler); | |
addEventListener('keyup', keyHandler); | |
// Create the main game object that contains all the state and on-screen characters | |
const game = new Game(document.getElementById('canvas')); | |
// creates the bullet while making a sound and decreasing the bullets counter | |
function fireBullet() { | |
game.sounds.playerShoots.currentTime = 0; | |
game.sounds.playerShoots.play(); | |
game.createBullet(); | |
game.options.numberOfBullets -= 1; | |
} | |
// reacts to the incomming key press data from the key handler module | |
function actOnKeyPress() { | |
if (rightPressed) { | |
game.hero.rotate(game.hero.speed); | |
} else if (leftPressed) { | |
game.hero.rotate(-game.hero.speed); | |
} | |
if (!firePressed) { | |
game.hero.firePaused = false; | |
} | |
if (firePressed && game.options.numberOfBullets > 0) { | |
if (!game.hero.firePaused) { | |
fireBullet(); | |
game.hero.firePaused = true; | |
} | |
} | |
} | |
function looseALife() { | |
console.log('You died!'); | |
setTimeout(() => { | |
game.hero.alive = true; | |
game.options.lives -= 1; | |
init(); | |
}, 2000); | |
} | |
function mainLoop() { | |
if (game.hero.alive) { | |
game.clearCanvas(); | |
game.text.draw( | |
game.ctx, | |
game.options.numberOfZombies, | |
game.options.numberOfBullets, | |
game.options.lives, | |
); | |
game.hero.draw(game.ctx); | |
game.detectCollision(); | |
game.animateBullets(); | |
game.animateZombies(); | |
actOnKeyPress(); | |
requestAnimationFrame(mainLoop); | |
} else { | |
looseALife(); | |
} | |
} | |
function init() { | |
game.options.numberOfZombies = 50; | |
game.options.numberOfBullets = 100; | |
game.zombies = []; | |
game.bullets = []; | |
game.createZombies(); | |
mainLoop(); | |
} | |
init(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment