A small project I did in spare time :) It should ressemble the old space invaders game, i know it's not perfect but I like it anyway.
A Pen by Patrick Stillhart on CodePen.
<p class="center">Space Invadors destroyed this page! Take revenge on them! | |
<br/> Use <span class="label label-danger">Space</span> to shoot and <span class="label label-danger">←</span> <span class="label label-danger">→</span> to move!   <button class="btn btn-default btn-xs" id="restart">Restart</button></p> | |
<canvas id="space-invaders"/> |
;(function() { | |
"use strict"; | |
// General | |
var canvas, | |
screen, | |
gameSize, | |
game; | |
// Assets | |
var invaderCanvas, | |
invaderMultiplier, | |
invaderSize = 20, | |
initialOffsetInvader, | |
invaderAttackRate, | |
invaderSpeed, | |
invaderSpawnDelay = 250; | |
// Counter | |
var i = 0, | |
kills = 0, | |
spawnDelayCounter = invaderSpawnDelay; | |
var invaderDownTimer; | |
// Text | |
var blocks = [ | |
[3, 4, 8, 9, 10, 15, 16], | |
[2, 4, 7, 11, 14, 16], | |
[1, 4, 7, 11, 13, 16], | |
[1, 2, 3, 4, 5, 7, 11, 13, 14, 15, 16, 17], | |
[4, 7, 11, 16], | |
[4, 8, 9, 10, 16] | |
]; | |
// Game Controller | |
// --------------- | |
var Game = function() { | |
this.level = -1; | |
this.lost = false; | |
this.player = new Player(); | |
this.invaders = []; | |
this.invaderShots = []; | |
if (invaderDownTimer === undefined) { | |
invaderDownTimer = setInterval(function() { | |
for (i = 0; i < game.invaders.length; i++) game.invaders[i].move(); | |
}, 1000 - (this.level * 1.8)); | |
}; | |
} | |
Game.prototype = { | |
update: function() { | |
// Next level | |
if (game.invaders.length === 0) { | |
spawnDelayCounter += 1; | |
if (spawnDelayCounter < invaderSpawnDelay) return; | |
this.level += 1; | |
invaderAttackRate -= 0.002; | |
invaderSpeed += 10; | |
game.invaders = createInvaders(); | |
spawnDelayCounter = 0; | |
} | |
if (!this.lost) { | |
// Collision | |
game.player.projectile.forEach(function(projectile) { | |
game.invaders.forEach(function(invader) { | |
if (collides(projectile, invader)) { | |
invader.destroy(); | |
projectile.active = false; | |
} | |
}); | |
}); | |
this.invaderShots.forEach(function(invaderShots) { | |
if (collides(invaderShots, game.player)) { | |
game.player.destroy(); | |
} | |
}); | |
for (i = 0; i < game.invaders.length; i++) game.invaders[i].update(); | |
} | |
// Don't stop player & projectiles.. they look nice | |
game.player.update(); | |
for (i = 0; i < game.invaderShots.length; i++) game.invaderShots[i].update(); | |
this.invaders = game.invaders.filter(function(invader) { | |
return invader.active; | |
}); | |
}, | |
draw: function() { | |
if (this.lost) { | |
screen.fillStyle = "rgba(0, 0, 0, 0.03)"; | |
screen.fillRect(0, 0, gameSize.width, gameSize.height); | |
screen.font = "55px Lucida Console"; | |
screen.textAlign = "center"; | |
screen.fillStyle = "white"; | |
screen.fillText("You lost", gameSize.width / 2, gameSize.height / 2); | |
screen.font = "20px Lucida Console"; | |
screen.fillText("Points: " + kills, gameSize.width / 2, gameSize.height / 2 + 30); | |
} else { | |
screen.clearRect(0, 0, gameSize.width, gameSize.height); | |
screen.font = "10px Lucida Console"; | |
screen.textAlign = "right"; | |
screen.fillText("Points: " + kills, gameSize.width, gameSize.height - 12); | |
} | |
screen.beginPath(); | |
var i; | |
this.player.draw(); | |
if (!this.lost) | |
for (i = 0; i < this.invaders.length; i++) this.invaders[i].draw(); | |
for (i = 0; i < this.invaderShots.length; i++) this.invaderShots[i].draw(); | |
screen.fill(); | |
}, | |
invadersBelow: function(invader) { | |
return this.invaders.filter(function(b) { | |
return Math.abs(invader.coordinates.x - b.coordinates.x) === 0 && | |
b.coordinates.y > invader.coordinates.y; | |
}).length > 0; | |
} | |
}; | |
// Invaders | |
// -------- | |
var Invader = function(coordinates) { | |
this.active = true; | |
this.coordinates = coordinates; | |
this.size = { | |
width: invaderSize, | |
height: invaderSize | |
}; | |
this.patrolX = 0; | |
this.speedX = invaderSpeed; | |
}; | |
Invader.prototype = { | |
update: function() { | |
if (Math.random() > invaderAttackRate && !game.invadersBelow(this)) { | |
var projectile = new Projectile({ | |
x: this.coordinates.x + this.size.width / 2, | |
y: this.coordinates.y + this.size.height - 5 | |
}, { | |
x: 0, | |
y: 2 | |
}); | |
game.invaderShots.push(projectile); | |
} | |
}, | |
draw: function() { | |
if (this.active) screen.drawImage(invaderCanvas, this.coordinates.x, this.coordinates.y); | |
}, | |
move: function() { | |
if (this.patrolX < 0 || this.patrolX > 100) { | |
this.speedX = -this.speedX; | |
this.patrolX += this.speedX; | |
this.coordinates.y += this.size.height; | |
if (this.coordinates.y + this.size.height * 2 > gameSize.height) game.lost = true; | |
} else { | |
this.coordinates.x += this.speedX; | |
this.patrolX += this.speedX; | |
} | |
}, | |
destroy: function() { | |
this.active = false; | |
kills += 1; | |
} | |
}; | |
// Player | |
// ------ | |
var Player = function() { | |
this.active = true; | |
this.size = { | |
width: 16, | |
height: 8 | |
}; | |
this.shooterHeat = -3; | |
this.coordinates = { | |
x: gameSize.width / 2 - (this.size.width / 2) | 0, | |
y: gameSize.height - this.size.height * 2 | |
}; | |
this.projectile = []; | |
this.keyboarder = new KeyController(); | |
}; | |
Player.prototype = { | |
update: function() { | |
for (var i = 0; i < this.projectile.length; i++) this.projectile[i].update(); | |
this.projectile = this.projectile.filter(function(projectile) { | |
return projectile.active; | |
}); | |
if (!this.active) return; | |
if (this.keyboarder.isDown(this.keyboarder.KEYS.LEFT) && this.coordinates.x > 0) this.coordinates.x -= 2; | |
else if (this.keyboarder.isDown(this.keyboarder.KEYS.RIGHT) && this.coordinates.x < gameSize.width - this.size.width) this.coordinates.x += 2; | |
if (this.keyboarder.isDown(this.keyboarder.KEYS.Space)) { | |
this.shooterHeat += 1; | |
if (this.shooterHeat < 0) { | |
var projectile = new Projectile({ | |
x: this.coordinates.x + this.size.width / 2 - 1, | |
y: this.coordinates.y - 1 | |
}, { | |
x: 0, | |
y: -7 | |
}); | |
this.projectile.push(projectile); | |
} else if (this.shooterHeat > 12) this.shooterHeat = -3; | |
} else { | |
this.shooterHeat = -3; | |
} | |
}, | |
draw: function() { | |
if (this.active) { | |
screen.rect(this.coordinates.x, this.coordinates.y, this.size.width, this.size.height); | |
screen.rect(this.coordinates.x - 2, this.coordinates.y + 2, 20, 6); | |
screen.rect(this.coordinates.x + 6, this.coordinates.y - 4, 4, 4); | |
} | |
for (var i = 0; i < this.projectile.length; i++) this.projectile[i].draw(); | |
}, | |
destroy: function() { | |
this.active = false; | |
game.lost = true; | |
} | |
}; | |
// Projectile | |
// ------ | |
var Projectile = function(coordinates, velocity) { | |
this.active = true; | |
this.coordinates = coordinates; | |
this.size = { | |
width: 3, | |
height: 3 | |
}; | |
this.velocity = velocity; | |
}; | |
Projectile.prototype = { | |
update: function() { | |
this.coordinates.x += this.velocity.x; | |
this.coordinates.y += this.velocity.y; | |
if (this.coordinates.y > gameSize.height || this.coordinates.y < 0) this.active = false; | |
}, | |
draw: function() { | |
if (this.active) screen.rect(this.coordinates.x, this.coordinates.y, this.size.width, this.size.height); | |
} | |
}; | |
// Keyboard input tracking | |
// ----------------------- | |
var KeyController = function() { | |
this.KEYS = { | |
LEFT: 37, | |
RIGHT: 39, | |
Space: 32 | |
}; | |
var keyCode = [37, 39, 32]; | |
var keyState = {}; | |
var counter; | |
window.addEventListener('keydown', function(e) { | |
for (counter = 0; counter < keyCode.length; counter++) | |
if (keyCode[counter] == e.keyCode) { | |
keyState[e.keyCode] = true; | |
e.preventDefault(); | |
} | |
}); | |
window.addEventListener('keyup', function(e) { | |
for (counter = 0; counter < keyCode.length; counter++) | |
if (keyCode[counter] == e.keyCode) { | |
keyState[e.keyCode] = false; | |
e.preventDefault(); | |
} | |
}); | |
this.isDown = function(keyCode) { | |
return keyState[keyCode] === true; | |
}; | |
}; | |
// Other functions | |
// --------------- | |
function collides(a, b) { | |
return a.coordinates.x < b.coordinates.x + b.size.width && | |
a.coordinates.x + a.size.width > b.coordinates.x && | |
a.coordinates.y < b.coordinates.y + b.size.height && | |
a.coordinates.y + a.size.height > b.coordinates.y; | |
} | |
function getPixelRow(rowRaw) { | |
var textRow = [], | |
placer = 0, | |
row = Math.floor(rowRaw / invaderMultiplier); | |
if (row >= blocks.length) return []; | |
for (var i = 0; i < blocks[row].length; i++) { | |
var tmpContent = blocks[row][i] * invaderMultiplier; | |
for (var j = 0; j < invaderMultiplier; j++) textRow[placer + j] = tmpContent + j; | |
placer += invaderMultiplier; | |
} | |
return textRow; | |
} | |
// Write Text | |
// ----------- | |
function createInvaders() { | |
var invaders = []; | |
var i = blocks.length * invaderMultiplier; | |
while (i--) { | |
var j = getPixelRow(i); | |
for (var k = 0; k < j.length; k++) { | |
invaders.push(new Invader({ | |
x: j[k] * invaderSize, | |
y: i * invaderSize | |
})); | |
} | |
} | |
return invaders; | |
} | |
// Start game | |
// ---------- | |
window.addEventListener('load', function() { | |
var invaderAsset = new Image; | |
invaderAsset.onload = function() { | |
invaderCanvas = document.createElement('canvas'); | |
invaderCanvas.width = invaderSize; | |
invaderCanvas.height = invaderSize; | |
invaderCanvas.getContext("2d").drawImage(invaderAsset, 0, 0); | |
// Game Creation | |
canvas = document.getElementById("space-invaders"); | |
screen = canvas.getContext('2d'); | |
initGameStart(); | |
loop(); | |
}; | |
invaderAsset.src = "//stillh.art/project/spaceInvaders/invader.gif"; | |
}); | |
window.addEventListener('resize', function() { | |
initGameStart(); | |
}); | |
document.getElementById('restart').addEventListener('click', function() { | |
initGameStart(); | |
}); | |
function initGameStart() { | |
if (window.innerWidth > 1200) { | |
screen.canvas.width = 1200; | |
screen.canvas.height = 500; | |
gameSize = { | |
width: 1200, | |
height: 500 | |
}; | |
invaderMultiplier = 3; | |
initialOffsetInvader = 420; | |
} else if (window.innerWidth > 800) { | |
screen.canvas.width = 900; | |
screen.canvas.height = 600; | |
gameSize = { | |
width: 900, | |
height: 600 | |
}; | |
invaderMultiplier = 2; | |
initialOffsetInvader = 280; | |
} else { | |
screen.canvas.width = 600; | |
screen.canvas.height = 300; | |
gameSize = { | |
width: 600, | |
height: 300 | |
}; | |
invaderMultiplier = 1; | |
initialOffsetInvader = 140; | |
} | |
kills = 0; | |
invaderAttackRate = 0.999; | |
invaderSpeed = 20; | |
spawnDelayCounter = invaderSpawnDelay; | |
game = new Game(); | |
} | |
function loop() { | |
game.update(); | |
game.draw(); | |
requestAnimationFrame(loop); | |
} | |
})(); |
A small project I did in spare time :) It should ressemble the old space invaders game, i know it's not perfect but I like it anyway.
A Pen by Patrick Stillhart on CodePen.
.center{text-align:center} | |
#space-invaders { | |
margin: 0 auto; | |
display: block; | |
background: white | |
} |
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet" /> |