Space-Invaders Controls Click on Game to Activate Controls. Refresh Page to Start New Game! Too follow along, you can use any editor, but I recommend NotePad++. It’s free and easy to use. Below is the Code Just ensure files are in single folder with subfolder of “img” for code to work. Place below images in “img” folder. Save images as “invader.png” and “spaceship.png” HTML Code for Web Page. Save file as .html <style> body { margin: 0; display: flex; justify-content: center; align-items: center; background-color: black; } </style> <div style="position: relative;"> <p style="position: absolute; z-index: 10; color: white; left: 10px; top: 10; margin: 0; font-family: sans-serif; font-size: 14px;" ><span>Score:</span><span id="scoreEl">0</span></p> <canvas></canvas></div> <script src="./index.js"></script> JavaScript Code. Save file as .js const scoreEl = document.querySelector('#scoreEl') const canvas = document.querySelector('canvas') const c = canvas.getContext('2d') console.log(scoreEl) canvas.width = 1024 canvas.height = 576 class Player { constructor() { this.position = { x: canvas.width / 2 - this.width / 2, y: 200 } this.velocity = { x: 0, y: 0 } this.rotation = 0 this.opacity = 1 const image = new Image() image.src = './img/spaceship.png' image.onload = () => { const scale = .15 this.image = image this.width = image.width * scale this.height = image.height * scale this.position = { x: canvas.width / 2 - this.width / 2, y: canvas.height - this.height - 20 } } } draw() { //c.fillStyle = 'red' //c.fillRect(this.position.x, this.position.y, this.width, this.height) c.save () c.globalAlpha = this.opacity c.translate( player.position.x + player.width / 2, player.position.y + player.height / 2 ) c.rotate(this.rotation) c.translate( -player.position.x - player.width / 2, -player.position.y - player.height / 2 ) c.drawImage( this.image, this.position.x, this.position.y, this.width, this.height ) c.restore() } update () { if (this.image) { this.draw() this.position.x += this.velocity.x } } } class Projectile { constructor({position, velocity}) { this.position = position this.velocity = velocity this.radius = 4 } draw() { c.beginPath() c.arc(this.position.x, this.position.y, this.radius, 0, Math.PI * 2) c.fillStyle = 'red' c.fill() c.closePath() } update() { this.draw() this.position.x += this.velocity.x this.position.y += this.velocity.y } } class Particle { constructor({position, velocity, radius, color, fades}) { this.position = position this.velocity = velocity this.radius = radius this.color = color this.opacity = 1 this.fades = fades } draw() { c.save() c.globalAlpha = this.opacity c.beginPath() c.arc(this.position.x, this.position.y, this.radius, 0, Math.PI * 2) c.fillStyle = this.color c.fill() c.closePath() c.restore() } update() { this.draw() this.position.x += this.velocity.x this.position.y += this.velocity.y if (this.fades) this.opacity -= 0.01 } } class InvaderProjectile { constructor({position, velocity}) { this.position = position this.velocity = velocity this.width = 3 this.height = 10 } draw() { c.fillStyle = 'white' c.fillRect(this.position.x, this.position.y, this.width, this.height) } update() { this.draw() this.position.x += this.velocity.x this.position.y += this.velocity.y } } class Invader { constructor({position}) { this.position = { x: canvas.width / 2 - this.width / 2, y: 200 } this.velocity = { x: 0, y: 0 } const image = new Image() image.src = './img/invader.png' image.onload = () => { const scale = 1 this.image = image this.width = image.width * scale this.height = image.height * scale this.position = { x: position.x, y: position.y } } } draw() { //c.fillStyle = 'red' //c.fillRect(this.position.x, this.position.y, this.width, this.height) c.drawImage( this.image, this.position.x, this.position.y, this.width, this.height ) } update ({velocity}) { if (this.image) { this.draw() this.position.x += velocity.x this.position.y += velocity.y } } shoot(invaderProjectiles) { invaderProjectiles.push(new InvaderProjectile({ position: { x: this.position.x + this.width /2, y: this.position.y + this.height }, velocity: { x: 0, y: 5 } })) } } class Grid { constructor() { this.position = { x: 0, y: 0 } this.velocity = { x: 3, y: 0 } this.invaders = [] const columns = Math.floor(Math.random() * 10 + 5) const rows = Math.floor(Math.random() * 5 + 2) this.width = columns * 30 for (let x = 0; x < columns; x++) { for (let y = 0; y < rows; y++) { this.invaders.push(new Invader({ position: { x: x * 30, y: y * 30 } }) ) } } console.log(this.invaders) } update () { this.position.x += this.velocity.x this.position.y += this.velocity.y this.velocity.y = 0 if (this.position.x + this.width >= canvas.width || this.position.x <= 0) { this.velocity.x = -this.velocity.x this.velocity.y = 30 } } } const player = new Player() const projectiles = [] const grids = [] const invaderProjectiles = [] const particles = [] const keys = { a: { pressed: false }, d: { pressed: false }, space: { pressed: false } } let frames = 0 let randomInterval = Math.floor(Math.random() * 500 + 500) let game = { over: false, active: true } let score = 0 for (let i = 0; i < 100; i++) { particles.push( new Particle ({ position: { x: Math.random() * canvas.width, y: Math.random() * canvas.height }, velocity: { x: 0, y:0.3 }, radius: Math.random() * 3, color: 'white' }) ) } function createParticles({object, color, fades}) { for (let i = 0; i < 15; i++) { particles.push( new Particle ({ position: { x: object.position.x + object.width /2, y: object.position.y + object.height /2 }, velocity: { x: (Math.random() - 0.5) *2, y: (Math.random() - 0.5) *2 }, radius: Math.random() * 3, color: color || '#BAA0DE', fades }) ) } } function animate () { if (!game.active) return requestAnimationFrame(animate) c.fillStyle = 'black' c.fillRect(0,0, canvas.width, canvas.height) player.update() particles.forEach ((particle, i) => { if (particle.position.y - particle.radius >= canvas.height) { particle.position.x = Math.random() * canvas.width particle.position.y = -particle.radius } if (particle.opacity <=0) { setTimeout(() => { particles.splice(i, 1) }, 0 ) } else { particle.update() } }) invaderProjectiles.forEach((invaderProjectile, index) => { if ( invaderProjectile.position.y + invaderProjectile.height >= canvas.height ) { setTimeout(() => { invaderProjectiles.splice(index, 1) }, 0) } else invaderProjectile.update() // projectile hits player if ( invaderProjectile.position.y + invaderProjectile.height >= player.position.y && invaderProjectile.position.x + invaderProjectile.width >= player.position.x && invaderProjectile.position.x <= player.position.x + player.width ) { console.log('you lose') setTimeout(() => { invaderProjectiles.splice(index, 1) player.opacity = 0 game.over = true }, 0) setTimeout(() => { game.active = false }, 2000) createParticles({ object: player, color: 'white', fades: true }) } }) projectiles.forEach((projectile, index) => { if (projectile.position.y + projectile.radius <= 0) { setTimeout(() => { projectiles.splice(index, 1) }, 0) } else { projectile.update() } }) grids.forEach((grid, gridIndex) => { grid.update() // spawn projectiles if (frames % 100 === 0 && grid.invaders.length > 0) { grid.invaders[Math.floor(Math.random() * grid.invaders.length)].shoot(invaderProjectiles) } grid.invaders.forEach ((invader, i) => { invader.update({velocity: grid.velocity}) //projectiles hit enemy projectiles.forEach((projectile, j) => { if ( projectile.position.y - projectile.radius <= invader.position.y + invader.height && projectile.position.x + projectile.radius >= invader.position.x && projectile.position.x - projectile.radius <= invader.position.x + invader.width && projectile.position.y + projectile.radius >= invader.position.y ) { setTimeout (() => { const invaderFound = grid.invaders.find( (invader2) => invader2 === invader ) const projectileFound = projectiles.find( (projectile2) => projectile2 === projectile ) // remove invader and projectile if (invaderFound && projectileFound){ score += 100 console.log(score) scoreEl.innerHTML = score createParticles({ object: invader, fades: true }) grid.invaders.splice(i, 1) projectiles.splice(j, 1) if (grid.invaders.length > 0) { const firstInvader = grid.invaders[0] const lastInvader = grid.invaders[grid. invaders.length - 1] grid.width = lastInvader.position.x - firstInvader.position.x + lastInvader.width grid.position.x = firstInvader.position.x } else { grids.splice(gridIndex, 1) } } }, 0) } }) }) }) if (keys.a.pressed && player.position.x >= 0) { player.velocity.x = -7 player.rotation = -.15 } else if (keys.d.pressed && player.position.x + player.width <= canvas.width) { player.velocity.x = 7 player.rotation = .15 }else { player.velocity.x = 0 player.rotation = 0 } console.log(frames) // spawning enemies if (frames % randomInterval === 0) { grids.push(new Grid()) randomInterval = Math.floor(Math.random() * 500 + 500) frames = 0 } frames++ } animate() addEventListener('keydown', ({key}) => { if (game.over) return switch (key) { case 'a': //console.log('left') keys.a.pressed = true break case 'd': //console.log('right') keys.d.pressed = true break case ' ': //console.log('space') projectiles.push(new Projectile({ position: { x: player.position.x + player.width / 2, y: player.position.y }, velocity: { x: 0, y: -10 } }) ) //console.log(projectiles) break } }) addEventListener('keyup', ({key}) => { switch (key) { case 'a': //console.log('left') keys.a.pressed = false break case 'd': //console.log('right') keys.d.pressed = false break case ' ': //console.log('space') break } })