Skip to content

Instantly share code, notes, and snippets.

@melanieimfeld
Last active March 9, 2023 18:05
Show Gist options
  • Save melanieimfeld/67db341f5cd698396fa298714d2c5e83 to your computer and use it in GitHub Desktop.
Save melanieimfeld/67db341f5cd698396fa298714d2c5e83 to your computer and use it in GitHub Desktop.
Space Invaders

Unfriendly 🛸

Written in JavaScript using the HTML canvas and the requestAnimationFrame method. Works best in Chrome or Firefox.

Game Elements & Functionalities

  • Gamecanvas: Can be started and stopped with space bar
  • Player: Can move left or right with < > keys
  • Opponent Ufos: 9 Colums x 4 Rows of opponents that bounce back and forth horizontally and move down vertically. If opponents collide with player, the game is over. It can be restarted with a window refresh.

File Structure

  • index.html: HTML template
  • game.js: Gamelogic
//bullets
// create new object
// create method draw & move
// move would move -y axis
// create a new instance when arrow up is pressed
// draw and move in animation loop
// remove when bullet left screen
//collision
//check if collides
//a.x + a.x + a.width/2 is inbetween b.x and b.x + b.width
//mark if collided
//filter if true
//global variables
const nCol = 9;
const nRow = 4;
const margin = 5;
const key = {
left: "ArrowLeft",
right: "ArrowRight",
space: "Space",
up: "ArrowUp"
};
var playerObj;
var objArray = [];
var bullArray = [];
var colWidth;
var keyPressed = false;
var paused = false;
var id;
//the game canvas
var gameCanvas = {
canvas: document.createElement("canvas"),
draw: function(){
this.parent = document.getElementById("wrap");
this.canvas.width = 600;
this.canvas.height = 600;
this.parent.appendChild(this.canvas);
this.context = this.canvas.getContext("2d");
this.audioBump = document.getElementById("bump");
this.audioMove = document.getElementById("move");
this.audioMove.setAttribute("preload", "true"); //currently disabled
this.audioBump.setAttribute("preload", "true");
},
start: function() {
this.frame = 0;
animate();
},
clear: function(){
this.context.clearRect(0,0,this.canvas.width,this.canvas.height);
},
stop: function(){
cancelAnimationFrame(id);
},
update: function(){
if(keyPressed == key.space){
paused = !paused;
if (paused){
console.log("start");
gameCanvas.start();
} else {
console.log("stop");
gameCanvas.stop();
}
}
},
gameOver: function() {
this.context.font = '50px Helvetica';
this.context.fillText("Gameover", this.canvas.width*0.3, this.canvas.height/2);
}
};
// the opponent (space invader)
function opponent(j,i,color,speed,colW) {
this.colWidth = colW;
this.color = color;
this.speed = speed;
this.colorLights = color;
this.width = 0.4 * this.colWidth;
this.height = 0.1 * this.colWidth;
this.x = j*this.colWidth + this.colWidth/2;
this.y = i*this.height * margin+40;
this.j = j;
this.moveY = 0;
}
//apply methods to prototype to only store them once
opponent.prototype = {
draw : function() {
var ctx = gameCanvas.context;
ctx.beginPath();
ctx.fillStyle = this.color;
ctx.ellipse(this.x, this.y, this.width, this.height, 0, 0, 2 * Math.PI);
ctx.ellipse(this.x, this.y - 6 , 10, 8, 0, 0, 2 * Math.PI);
ctx.fill();
ctx.beginPath();
ctx.fillStyle = this.colorLights;
ctx.arc(this.x, this.y, 2, 0, 2 * Math.PI);
ctx.arc(this.x-10, this.y, 2, 0, 2 * Math.PI);
ctx.arc(this.x+10, this.y, 2, 0, 2 * Math.PI);
ctx.fill();
},
move : function() {
this.colorLights = "white";
this.x -= this.speed;
if(this.x - this.width < this.j * this.colWidth|| this.x + this.width > (this.j+1)* this.colWidth){ //if opponent reaches boundary, reverse speed
this.speed = -this.speed;
if(this.moveY % 8 === 0 && this.moveY != 0){ //move in +y direction
this.y += 20;
//gameCanvas.audioBump.play();
}
this.moveY ++;
}
if (this.x > (this.j * this.colWidth + this.colWidth/2)-3 && this.x < (this.j * this.colWidth + this.colWidth/2)+3){ //if x lies within a certain area, blink
this.colorLights = "red";
}
}
};
// the player
function player(x,y,width,height,color,speed){
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.color = color;
this.speed = speed;
}
player.prototype = {
draw: function(){
var ctx = gameCanvas.context;
ctx.beginPath();
ctx.rect(this.x, this.y, this.width, this.height);
ctx.arc(this.x + this.width/2, this.y, 12, 0, 2 * Math.PI);
ctx.rect(this.x + this.width/2 - 3, this.y - 24, 6, 13);
ctx.fillStyle = this.color;
ctx.fill();
ctx.closePath();
},
move: function() {
if(keyPressed == key.left && this.x > 0){ this.x -= this.speed;}
else if (keyPressed == key.right && this.x + this.width < gameCanvas.canvas.width) { this.x += this.speed;}
else if(keyPressed == key.up){
bullArray.push(new Bullet(this.x + this.width /2 -2, this.y, 4,10));
}
}
};
function Bullet(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.speed = 6;
}
Bullet.prototype = {
draw : function() {
var ctx = gameCanvas.context;
ctx.fillStyle = "white";
ctx.rect(this.x,this.y,this.width,this.height);
ctx.fill();
},
move : function() {
this.y -= this.speed;
}
};
//when DOM is loaded, instantiate gamevanvas and gameelements
function init() {
gameCanvas.draw();
playerObj = new player(gameCanvas.canvas.width/2 - 50,gameCanvas.canvas.height - 80,100,40, "white",6);
objArray = populateOpponents(nCol, nRow);
}
//clear screen, update position and draw all game items
function animate() {
//if any item in array collides with player, reset game
var collidingWithPlayer = objArray.filter(item => item.y + item.height > playerObj.y - 24);
bullArray = bullArray.filter(item => item.y > 0);
console.log(bullArray);
if(collidingWithPlayer.length > 0) {
gameCanvas.clear();
gameCanvas.gameOver();
} else {
gameCanvas.clear();
playerObj.move();
playerObj.draw();
objArray.forEach(function(item){
item.move();
item.draw();
});
bullArray.forEach( function(item){
item.move();
item.draw();
});
gameCanvas.frame ++;
id = requestAnimationFrame(animate);
}
}
//function to populate opponents in an nCol x nRow grid
function populateOpponents(nCol, nRow){
var colWidth = gameCanvas.canvas.width / nCol;
var array = [];
for (var i=0; i < nRow; i++){
for(var j=0; j < nCol; j++){
array.push(new opponent(j,i, "white",0.4, colWidth));
}
}
return array;
}
//event listeners for key events
window.addEventListener("keydown", function(e) {
keyPressed = e.code;
gameCanvas.update();
}, false);
window.addEventListener("keyup", function(e) {
keyPressed = false;
}, false);
<html>
<head>
<title>Space Invaders</title>
<style type="text/css">
body {
margin: 0 auto;
padding: 0;
color: white;
font-family: "Helvetica";
background: linear-gradient(#1b2f92, #E8CAD5);
}
/* FF only*/
@-moz-document url-prefix() {
body {
height: 100%;
}
}
#wrap {
text-align: center;
margin-top: 30px;
}
p,h1 {
margin: 0;
}
canvas {
margin-top: 30px;
max-width: 100%;
height: auto;
background: linear-gradient(#1b2f92, #E8CAD5);
box-shadow:5px 5px 10px black;
}
</style>
</head>
<body onload = "init()">
<script src = "game.js"></script>
<div id="wrap">
<h1>Unfriendly &#128760;</h1>
<p>Press S to start or pause. Press &larr; &rarr; to move and &uarr; to shoot.</p>
<audio id="move" src="fastinvader1.wav">Your browser does not support audio elements</audio>
<audio id="bump" src="bump.mp3">Your browser does not support audio elements</audio>
</div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment