Created
October 15, 2012 11:37
-
-
Save bpierre/3892044 to your computer and use it in GitHub Desktop.
SUPER #7
This file contains hidden or 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
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<title>SUPER #7</title> | |
<style> | |
html { display: table; width: 100%; height: 100%; background: #000; } | |
body { display: table-cell; vertical-align:middle; text-align:center; margin: 0; } | |
canvas { margin: 0; } | |
h1 { color: #fff; margin: 0 0 10px; font:24px sans-serif; } | |
</style> | |
</head> | |
<body> | |
<h1>SUPER #7<span id="lvl"></span></h1> | |
<canvas width="500" height="500" id="c"></canvas> | |
<script src="http://pierrebertet.net/tmp/7/raf.js"></script> | |
<script src="main.js"></script> | |
</body> | |
</html> |
This file contains hidden or 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
(function(window, document){ | |
'use strict'; | |
var canv = document.getElementById('c'), | |
ctx = canv.getContext('2d'), | |
levelElt = document.getElementById('lvl'), | |
baseUrl = 'http://pierrebertet.net/tmp/7/', | |
MUTE_SOUND = false, | |
MAX_FPS = 60, | |
GAME_SPEED = 20, | |
INSTRUCTIONS = [ | |
'YOU ARE THE GREEN GUY.', | |
'THERE IS A GREY BOSS.', | |
'YOU HAVE TO KILL HIM.', | |
'BUT HE HAS TO KILL YOU TOO.', | |
'AVOID ITS BULLETS. KILL HIM FIRST.', | |
'GOOD LUCK.', | |
'PRESS [SPACE]' | |
], | |
PLAYER_SPEED = 4, | |
BULLET_MIN_SPEED = 3, | |
BULLET_MAX_SPEED = 3, | |
BULLET_RATE = 200, | |
MONSTER_MIN_SPEED = 1, | |
MONSTER_MAX_SPEED = 3, | |
MONSTER_RATE = 1000, | |
BOSS_SPEED = 1, | |
LEVELS = [ | |
[BULLET_MIN_SPEED, BULLET_MAX_SPEED, BULLET_RATE, MONSTER_MIN_SPEED, MONSTER_MAX_SPEED, MONSTER_RATE, BOSS_SPEED], | |
[3, 3, 200, 2, 3, 800, 1], | |
[3, 3, 300, 2, 3, 600, 1], | |
[3, 3, 300, 2, 4, 500, 1], | |
[3, 3, 400, 2, 4, 400, 2], | |
[3, 3, 500, 3, 5, 300, 2], | |
[3, 3, 600, 3, 5, 200, 2], | |
[3, 3, 700, 3, 5, 100, 2], | |
[3, 3, 800, 3, 5, 100, 2], | |
[3, 3, 900, 3, 5, 80, 2] | |
], | |
KEYS = { | |
LEFT: 37, | |
RIGHT: 39, | |
UP: 38, | |
DOWN: 40, | |
SPACE: 32 | |
}, | |
PLAYER_SIZE = 11, | |
BULLET_SIZE = 5, | |
BOSS_SIZE = 11, | |
MONSTER_SIZE = 5, | |
IMAGES = ['boss', 'player', 'bullet', 'monster'], | |
SOUNDS = ['bullet', 'monster', 'death', 'level', 'win'], | |
level = {}, | |
pressedKeys = { | |
left: false, | |
right: false, | |
up: false, | |
down: false | |
}, | |
canvWidth = canv.width, | |
canvHeight = canv.height, | |
getRandomInt = function(min, max, excludes) { | |
var result = Math.floor(Math.random() * (max - min + 1)) + min; | |
if (excludes && excludes.indexOf(result) !== -1) { | |
return getRandomInt(min, max, excludes); | |
} | |
return result; | |
}, | |
lastRenderTime = null, | |
game = null; | |
canv.style.background = '#1D1F23'; | |
document.addEventListener('keydown', function(e){ | |
switch (e.keyCode) { | |
case KEYS.LEFT: | |
pressedKeys.left = true; | |
break; | |
case KEYS.RIGHT: | |
pressedKeys.right = true; | |
break; | |
case KEYS.UP: | |
pressedKeys.up = true; | |
break; | |
case KEYS.DOWN: | |
pressedKeys.down = true; | |
break; | |
} | |
}, false); | |
document.addEventListener('keyup', function(e){ | |
switch (e.keyCode) { | |
case KEYS.LEFT: | |
pressedKeys.left = false; | |
break; | |
case KEYS.RIGHT: | |
pressedKeys.right = false; | |
break; | |
case KEYS.UP: | |
pressedKeys.up = false; | |
break; | |
case KEYS.DOWN: | |
pressedKeys.down = false; | |
break; | |
} | |
}, false); | |
var setLevel = function(levelNum) { | |
if (LEVELS.length < levelNum + 1) { | |
return false; | |
} | |
levelElt.innerHTML = ' - LEVEL ' + (levelNum + 1); | |
level.num = levelNum; | |
level.BULLET_MIN_SPEED = LEVELS[levelNum][0]; | |
level.BULLET_MAX_SPEED = LEVELS[levelNum][1]; | |
level.BULLET_RATE = LEVELS[levelNum][2]; | |
level.MONSTER_MIN_SPEED = LEVELS[levelNum][3]; | |
level.MONSTER_MAX_SPEED = LEVELS[levelNum][4]; | |
level.MONSTER_RATE = LEVELS[levelNum][5]; | |
level.BOSS_SPEED = LEVELS[levelNum][6]; | |
return true; | |
}; | |
var getImage = (function(){ | |
var loadedImages = {}; | |
for (var i=0; i < IMAGES.length; i++) { | |
loadedImages[IMAGES[i]] = document.createElement('img'); | |
loadedImages[IMAGES[i]].src = baseUrl + 'img/' + IMAGES[i] + '.png'; | |
} | |
return function(name){ | |
return loadedImages[name]; | |
}; | |
})(); | |
var playSound = (function(){ | |
var loadedSounds = {}; | |
if (!MUTE_SOUND) { | |
for (var i=0; i < SOUNDS.length; i++) { | |
loadedSounds[SOUNDS[i]] = document.createElement('audio'); | |
loadedSounds[SOUNDS[i]].preload = 'auto'; | |
loadedSounds[SOUNDS[i]].controls = false; | |
loadedSounds[SOUNDS[i]].src = baseUrl + 'sound/' + SOUNDS[i] + '.ogg'; | |
} | |
} | |
return function(name){ | |
if (MUTE_SOUND) { | |
return; | |
} | |
if (loadedSounds[name].readyState > 0) { | |
loadedSounds[name].pause(); | |
loadedSounds[name].currentTime = 0; | |
} | |
loadedSounds[name].play(); | |
}; | |
})(); | |
var drawOverlay = function(text, base) { | |
ctx.fillStyle = 'rgba(0, 0, 0, 0.5)'; | |
ctx.fillRect(0, 0, canvWidth, canvHeight); | |
ctx.fillStyle = '#FFF'; | |
ctx.font = "24px sans-serif"; | |
ctx.textAlign = 'center'; | |
for (var i = text.length - 1; i >= 0; i--){ | |
ctx.fillText(text[i], canvWidth/2, canvHeight/2 - 150 + (base || 0) + (50 * i)); | |
} | |
}; | |
var Entity = function(x, y){ | |
this.x = 0; | |
this.y = 0; | |
this.width = 10; | |
this.height = 10; | |
this.limits = { | |
top: 0, | |
bottom: canvHeight - this.height, | |
left: 0, | |
right: canvWidth - this.width | |
}; | |
if (x) { | |
this.setX(x); | |
} | |
if (y) { | |
this.setY(y); | |
} | |
}; | |
Entity.prototype.setX = function(x) { | |
if (x < this.limits.left) { | |
this.x = this.limits.left; | |
return false; | |
} else if (x > this.limits.right) { | |
this.x = this.limits.right; | |
return false; | |
} else { | |
this.x = x; | |
return true; | |
} | |
}; | |
Entity.prototype.setY = function(y) { | |
if (y < this.limits.top) { | |
this.y = this.limits.top; | |
return false; | |
} else if (y > this.limits.bottom) { | |
this.y = this.limits.bottom; | |
return false; | |
} else { | |
this.y = y; | |
return true; | |
} | |
}; | |
Entity.prototype.addX = function(x) { | |
return this.setX(this.x + x); | |
}; | |
Entity.prototype.addY = function(y) { | |
return this.setY(this.y + y); | |
}; | |
Entity.prototype.collide = function(entity) { | |
if ((this.x + this.width) < entity.x) { | |
return false; | |
} | |
if (this.x > (entity.x + entity.width)) { | |
return false; | |
} | |
if ((this.y + this.height) < entity.y) { | |
return false; | |
} | |
if (this.y > (entity.y + entity.height)) { | |
return false; | |
} | |
return true; | |
}; | |
var nextLevel = 0; | |
var startGame = function(gameover, win) { | |
setLevel(nextLevel); | |
// Player | |
var player = (function(){ | |
var entity = new Entity(PLAYER_SIZE, PLAYER_SIZE); | |
entity.bullets = []; | |
entity.width = entity.height = PLAYER_SIZE; | |
entity.popRate = level.BULLET_RATE; | |
entity.lastPop = null; | |
entity.popBullet = function(xSpeed, ySpeed){ | |
var bulletPos = [entity.x + Math.round(entity.width/2 - BULLET_SIZE/2), entity.y + Math.round(entity.height/2 - BULLET_SIZE/2)], | |
bullet = null; | |
if (xSpeed > 0) { | |
bulletPos[0] = entity.x + entity.width; | |
} else if (xSpeed < 0) { | |
bulletPos[0] = entity.x - BULLET_SIZE; | |
} | |
if (ySpeed > 0) { | |
bulletPos[1] = entity.y + entity.height; | |
} else if (ySpeed < 0) { | |
bulletPos[1] = entity.y - BULLET_SIZE; | |
} | |
bullet = new Entity(bulletPos[0], bulletPos[1]); | |
bullet.width = bullet.height = BULLET_SIZE; | |
bullet.limits.left = -bullet.width; | |
bullet.limits.right = canvWidth; | |
bullet.limits.top = -bullet.height; | |
bullet.limits.bottom = canvHeight; | |
bullet.xSpeed = xSpeed; | |
bullet.ySpeed = ySpeed; | |
bullet.draw = function(){ | |
ctx.drawImage(getImage('bullet'), bullet.x, bullet.y, bullet.width, bullet.height); | |
}; | |
entity.bullets.push(bullet); | |
playSound('bullet'); | |
}; | |
entity.draw = function(){ | |
ctx.drawImage(getImage('player'), entity.x, entity.y, entity.width, entity.height); | |
}; | |
return entity; | |
})(); | |
// Boss | |
var boss = (function(){ | |
var initialPosition = (function(){ | |
if (getRandomInt(0, 1)) { | |
return [getRandomInt(100, canvWidth - BOSS_SIZE), getRandomInt(0, canvHeight - BOSS_SIZE)]; | |
} else { | |
return [getRandomInt(0, canvWidth - BOSS_SIZE), getRandomInt(100, canvHeight - BOSS_SIZE)]; | |
} | |
})(); | |
var entity = new Entity(initialPosition[0], initialPosition[1]); | |
entity.monsters = []; | |
entity.width = entity.height = BOSS_SIZE; | |
entity.popRate = level.MONSTER_RATE; | |
entity.lastPop = null; | |
entity.target = { | |
x: entity.x, | |
y: entity.y | |
}; | |
entity.popMonster = function(){ | |
var xSpeed = getRandomInt(-level.MONSTER_MAX_SPEED, level.MONSTER_MAX_SPEED, [level.BOSS_SPEED]), | |
ySpeed = getRandomInt(-level.MONSTER_MAX_SPEED, level.MONSTER_MAX_SPEED, [level.BOSS_SPEED]), | |
pos = [entity.x + Math.round(entity.width/2 - MONSTER_SIZE/2), entity.y + Math.round(entity.height/2 - MONSTER_SIZE/2)], | |
monster = null; | |
// Force movement | |
if (!xSpeed && !ySpeed) { | |
if (getRandomInt(0, 1)) { | |
xSpeed = getRandomInt(-level.MONSTER_MAX_SPEED, level.MONSTER_MAX_SPEED, [0, level.BOSS_SPEED]); | |
} else { | |
ySpeed = getRandomInt(-level.MONSTER_MAX_SPEED, level.MONSTER_MAX_SPEED, [0, level.BOSS_SPEED]); | |
} | |
} | |
if (xSpeed > 0) { | |
pos[0] = entity.x + entity.width + MONSTER_SIZE; | |
} else if (xSpeed < 0) { | |
pos[0] = entity.x - BULLET_SIZE; | |
} | |
if (ySpeed > 0) { | |
pos[1] = entity.y + entity.height + MONSTER_SIZE; | |
} else if (ySpeed < 0) { | |
pos[1] = entity.y - MONSTER_SIZE; | |
} | |
monster = new Entity(pos[0], pos[1]); | |
monster.width = monster.height = MONSTER_SIZE; | |
monster.limits.left = -MONSTER_SIZE; | |
monster.limits.right = canvWidth; | |
monster.limits.top = -MONSTER_SIZE; | |
monster.limits.bottom = canvHeight; | |
monster.xSpeed = xSpeed; | |
monster.ySpeed = ySpeed; | |
monster.draw = function() { | |
ctx.drawImage(getImage('monster'), monster.x, monster.y, monster.width, monster.height); | |
}; | |
entity.monsters.push(monster); | |
playSound('monster'); | |
}; | |
entity.draw = function(){ | |
ctx.drawImage(getImage('boss'), entity.x, entity.y, entity.width, entity.height); | |
}; | |
return entity; | |
})(); | |
var loopTimer = 0, reqAnimId = 0; | |
var game = { | |
loop: function(){ | |
var now = Date.now(); | |
var plSpeed = PLAYER_SPEED; | |
if ((pressedKeys.left || pressedKeys.right) && (pressedKeys.up || pressedKeys.down)) { | |
plSpeed = plSpeed / 2; | |
} | |
if (pressedKeys.left) { | |
player.addX(-plSpeed); | |
} else if (pressedKeys.right) { | |
player.addX(plSpeed); | |
} | |
if (pressedKeys.up) { | |
player.addY(-plSpeed); | |
} else if (pressedKeys.down) { | |
player.addY(plSpeed); | |
} | |
// Add bullet | |
var bulletSpeedX = 0, bulletSpeedY = 0; | |
if (!player.lastPop || now - player.lastPop > player.popRate) { | |
if (pressedKeys.left) { | |
bulletSpeedX = getRandomInt(-level.BULLET_MAX_SPEED, -level.BULLET_MIN_SPEED); | |
} else if (pressedKeys.right) { | |
bulletSpeedX = getRandomInt(level.BULLET_MIN_SPEED, level.BULLET_MAX_SPEED); | |
} | |
if (pressedKeys.up) { | |
bulletSpeedY = getRandomInt(-level.BULLET_MAX_SPEED, -level.BULLET_MIN_SPEED); | |
} else if (pressedKeys.down) { | |
bulletSpeedY = getRandomInt(level.BULLET_MIN_SPEED, level.BULLET_MAX_SPEED); | |
} | |
if (bulletSpeedX || bulletSpeedY) { | |
player.popBullet(bulletSpeedX, bulletSpeedY); | |
} | |
player.lastPop = now; | |
} | |
// Move bullets | |
var bulletsMoveX, bulletsMoveY, bullets = player.bullets; | |
for (var bi = bullets.length - 1; bi >= 0; bi--) { | |
if (bullets[bi].xSpeed !== 0 && bullets[bi].ySpeed !== 0) { | |
bulletsMoveX = bullets[bi].addX(bullets[bi].xSpeed/2); | |
bulletsMoveY = bullets[bi].addY(bullets[bi].ySpeed/2); | |
} else { | |
bulletsMoveX = bullets[bi].addX(bullets[bi].xSpeed); | |
bulletsMoveY = bullets[bi].addY(bullets[bi].ySpeed); | |
} | |
// Bullet / boss collision | |
if (bullets[bi].collide(boss)) { | |
return win(); | |
} | |
if (!bulletsMoveX || !bulletsMoveY) { | |
// Remove the bullet from the bullets list | |
bullets.splice(bullets.indexOf(bullets[bi]), 1); | |
} | |
} | |
// Move monsters | |
var xMove, yMove, monsters = boss.monsters; | |
for (var i = monsters.length - 1; i >= 0; i--) { | |
if (monsters[i].xSpeed !== 0 && monsters[i].xSpeed !== 0) { | |
xMove = monsters[i].addX(monsters[i].xSpeed/2); | |
yMove = monsters[i].addY(monsters[i].ySpeed/2); | |
} else { | |
xMove = monsters[i].addX(monsters[i].xSpeed); | |
yMove = monsters[i].addY(monsters[i].ySpeed); | |
} | |
// Player / monster collision | |
if (player.collide(monsters[i])) { | |
return gameover(); | |
} | |
if (!xMove || !yMove) { | |
// Remove the monster from the monsters list | |
monsters.splice(monsters.indexOf(monsters[i]), 1); | |
} | |
} | |
// Add monster | |
if (!boss.lastPop || now - boss.lastPop > boss.popRate) { | |
boss.popMonster(); | |
boss.lastPop = now; | |
} | |
// Move boss | |
if (boss.target.x === boss.x || boss.target.y === boss.y) { | |
boss.target.x = getRandomInt(boss.limits.left, boss.limits.right); | |
boss.target.y = getRandomInt(boss.limits.top, boss.limits.bottom); | |
} else { | |
if (boss.target.x < boss.x) { | |
boss.addX(-level.BOSS_SPEED); | |
// Prevent boss hover | |
if (boss.target.x > boss.x) { | |
boss.x = boss.target.x; | |
} | |
} else if (boss.target.x > boss.x) { | |
boss.addX(level.BOSS_SPEED); | |
// Prevent boss hover | |
if (boss.target.x < boss.x) { | |
boss.x = boss.target.x; | |
} | |
} | |
if (boss.target.y < boss.y) { | |
boss.addY(-level.BOSS_SPEED); | |
// Prevent boss hover | |
if (boss.target.y > boss.y) { | |
boss.y = boss.target.y; | |
} | |
} else if (boss.target.y > boss.y) { | |
boss.addY(level.BOSS_SPEED); | |
// Prevent boss hover | |
if (boss.target.y < boss.y) { | |
boss.y = boss.target.y; | |
} | |
} | |
} | |
// Boss / player collision | |
if (player.collide(boss)) { | |
return gameover(); | |
} | |
loopTimer = window.setTimeout(game.loop, GAME_SPEED); | |
}, | |
draw: function(last){ | |
var now = Date.now(); | |
// Max fps | |
if (last !== true && lastRenderTime && now - lastRenderTime < 1000 / MAX_FPS) { | |
reqAnimId = window.requestAnimationFrame(game.draw); | |
return; | |
} | |
// Save the last rendering time (to lock the max. framerate) | |
lastRenderTime = now; | |
// Clear | |
canv.width = canv.width; | |
// Bullets | |
for (var i = player.bullets.length - 1; i >= 0; i--) { | |
player.bullets[i].draw(); | |
} | |
// Player | |
player.draw(); | |
// Monsters | |
for (var j = boss.monsters.length - 1; j >= 0; j--) { | |
boss.monsters[j].draw(); | |
} | |
// Boss | |
boss.draw(); | |
if (last !== true) { | |
reqAnimId = window.requestAnimationFrame(game.draw); | |
} | |
}, | |
stop: function(){ | |
game.draw(true); | |
window.clearTimeout(loopTimer); | |
window.cancelAnimationFrame(reqAnimId); | |
lastRenderTime = 0; | |
} | |
}; | |
return game; | |
}; | |
var initOverlay = function(text, base) { | |
drawOverlay(text, base); | |
document.addEventListener('keypress', initGame, false); | |
}; | |
var initGame = function(e){ | |
if (e.charCode === KEYS.SPACE) { | |
document.removeEventListener('keypress', initGame, false); | |
game = startGame( | |
// Game over | |
function(){ | |
playSound('death'); | |
game.stop(); | |
game = null; | |
nextLevel = 0; | |
initOverlay(['YOU LOSE AT LEVEL', 'HE WINS.', 'HA HA HA.'], 80); | |
}, | |
// Win | |
function(){ | |
game.stop(); | |
game = null; | |
if (level.num < LEVELS.length-1) { | |
playSound('level'); | |
nextLevel = level.num + 1; | |
initOverlay(['YOU WIN!', 'READY FOR THE NEXT LEVEL?', 'PRESS [SPACE]'], 60); | |
} else { | |
playSound('win'); | |
nextLevel = 0; | |
initOverlay(['★★★ THE END ★★★', 'START AGAIN?', 'PRESS [SPACE]'], -40); | |
} | |
}); | |
game.loop(); | |
game.draw(); | |
} | |
}; | |
initOverlay(INSTRUCTIONS); | |
})(window, document); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment