Last active
December 22, 2015 16:29
-
-
Save killwing/6499826 to your computer and use it in GitHub Desktop.
a HTML5 canvas based simplified copycat of the shooting game in Wechat 5.0
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> | |
<head> | |
<title>striker</title> | |
</head> | |
<body align="center" style="margin:0;"> | |
<canvas id="canvas" width="1500" height="900"></canvas> | |
<script> | |
// class inherit | |
function inherits(ctor, superCtor) { | |
ctor.super_ = superCtor; | |
ctor.prototype = Object.create(superCtor.prototype, { | |
constructor: { | |
value: ctor, | |
enumerable: false, | |
writable: true, | |
configurable: true | |
} | |
}); | |
}; | |
// collision checking | |
function pixelCollide(object1, object2) { | |
var xMin = Math.max(object1.x, object2.x); | |
var yMin = Math.max(object1.y, object2.y); | |
var xMax = Math.min(object1.x + object1.width, object2.x + object2.width); | |
var yMax = Math.min(object1.y + object1.height, object2.y + object2.height); | |
for (var x = xMin; x < xMax; ++x) { | |
for (var y = yMin; y < yMax; ++y) { | |
var pixel1 = ((x - object1.x) + (y - object1.y) * object1.width) * 4 + 3; | |
var pixel2 = ((x - object2.x) + (y - object2.y) * object2.width) * 4 + 3; | |
if (object1.data[pixel1] !== 0 && object2.data[pixel2] !== 0) { | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
function boundingBoxCollide(object1, object2) { | |
var top1 = object1.y; | |
var left1 = object1.x; | |
var bottom1 = object1.y + object1.height; | |
var right1 = object1.x + object1.width; | |
var top2 = object2.y; | |
var left2 = object2.x; | |
var bottom2 = object2.y + object2.height; | |
var right2 = object2.x + object2.width; | |
if (bottom1 < top2 || top1 > bottom2 || right1 < left2 || left1 > right2) { | |
return false; | |
} | |
return true; | |
}; | |
function checkCollide(object1, object2, accurate) { | |
if (boundingBoxCollide(object1, object2)) { | |
if (accurate) { | |
return pixelCollide(object1, object2); | |
} | |
return true; | |
} | |
return false; | |
} | |
// for sprites image | |
function Sprite(img, area) { | |
this.x = 0; | |
this.y = 0; | |
this.area = area; | |
this.width = area.w; | |
this.height = area.h; | |
this.img = img; | |
context.clearRect(0, 0, context.canvas.width, context.canvas.height); | |
context.drawImage(img, area.x, area.y, area.w, area.h, 0, 0, area.w, area.h); | |
this.data = context.getImageData(0, 0, area.w, area.h).data; | |
context.clearRect(0, 0, context.canvas.width, context.canvas.height); | |
}; | |
Sprite.prototype.draw = function(pos, center) { | |
this.x = pos.x; | |
this.y = pos.y; | |
center = center || true; | |
if (center) { | |
context.drawImage(this.img, this.area.x, this.area.y, this.area.w, this.area.h, | |
pos.x - this.area.w/2, pos.y - this.area.h/2, this.area.w, this.area.h); | |
} else { | |
context.drawImage(this.img, this.area.x, this.area.y, this.area.w, this.area.h, | |
pos.x, pos.y, this.area.w, this.area.h); | |
} | |
}; | |
// for single image | |
function Img(src) { | |
var self = this; | |
var img = new Image; | |
img.src = src; | |
img.addEventListener('load', function() { | |
Sprite.call(self, img, {x: 0, y: 0, w: img.width, h: img.height}); | |
}, false); | |
}; | |
inherits(Img, Sprite); | |
// vector | |
function Vector(x, y) { | |
this.set(x, y); | |
}; | |
Vector.random2D = function() { | |
var r = Math.random() * 2 * Math.PI; | |
return new Vector(Math.cos(r), Math.sin(r)); | |
}; | |
Vector.add = function(v1, v2) { | |
return new Vector(v1.x + v2.x, v1.y + v2.y); | |
}; | |
Vector.sub = function(v1, v2) { | |
return new Vector(v1.x - v2.x, v1.y - v2.y); | |
}; | |
Vector.prototype.set = function(x, y) { | |
if (typeof x === 'object' && typeof y === 'undefined') { | |
this.x = x.x; | |
this.y = x.y; | |
} else { | |
this.x = x; | |
this.y = y; | |
} | |
}; | |
Vector.prototype.debug = function() { | |
console.debug('x: ', this.x, 'y: ', this.y); | |
}; | |
Vector.prototype.add = function(v) { | |
this.x += v.x; | |
this.y += v.y; | |
}; | |
Vector.prototype.sub = function(v) { | |
this.x -= v.x; | |
this.y -= v.y; | |
}; | |
Vector.prototype.mult = function(n) { | |
this.x *= n; | |
this.y *= n; | |
}; | |
Vector.prototype.div = function(n) { | |
this.x /= n; | |
this.y /= n; | |
}; | |
Vector.prototype.mag = function(v) { | |
return Math.sqrt(this.x * this.x + this.y * this.y); | |
}; | |
Vector.prototype.normalize = function(v) { | |
var m = this.mag(); | |
if (m != 0) { | |
this.div(m); | |
} | |
}; | |
Vector.prototype.limit = function(max) { | |
if (this.mag() > max) { | |
this.normalize(); | |
this.mult(max); | |
} | |
}; | |
// moving object | |
function Mover(img, l, d, s) { | |
this.img = img | |
this.l = l; | |
this.d = d; | |
this.s = s; | |
}; | |
Mover.prototype.update = function() { | |
this.doUpdate && this.doUpdate(); | |
var dir = Vector.sub(this.d, this.l); | |
dir.limit(this.s); | |
this.l.add(dir); | |
}; | |
Mover.prototype.draw = function() { | |
this.img.draw(this.l); | |
}; | |
function Movers(create, n, interval) { | |
var self = this; | |
this.movers = []; | |
this.create = create; | |
if (typeof n !== 'undefined' && typeof interval !== 'undefined') { | |
var timer = setInterval(function() { | |
self.movers.push(create()); | |
if (--n == 0) { | |
clearInterval(timer); | |
} | |
}, interval); | |
} | |
}; | |
Movers.prototype.update = function() { | |
this.doUpdate && this.doUpdate(); | |
this.movers.forEach(function(m) { | |
m.update(); | |
}); | |
}; | |
Movers.prototype.draw = function() { | |
this.movers.forEach(function(m) { | |
m.draw(); | |
}); | |
}; | |
// player fighter | |
function Player(spt, sptAlt, sptEx, speed) { | |
Mover.call(this, spt, new Vector(options.startLoc), mouse, speed); | |
this.imgAlt = sptAlt; | |
this.alt = false; | |
this.imgEx = sptEx; | |
this.hit = false; | |
this.exFrame = 0; | |
}; | |
inherits(Player, Mover); | |
Player.prototype.draw = function() { | |
if (!this.hit) { | |
if (this.alt) { | |
this.img.draw(this.l); | |
} else { | |
this.imgAlt.draw(this.l); | |
} | |
this.alt = !this.alt; | |
} else { | |
if (this.exFrame == this.imgEx.length*5) { | |
game.state = 'over'; | |
return; | |
} | |
this.imgEx[Math.floor(this.exFrame/5)].draw(this.l); | |
this.exFrame++; | |
} | |
}; | |
// player's bullet | |
function Bullet(spt, playerLoc, speed) { | |
this.topend = -50; | |
Mover.call(this, spt, new Vector(playerLoc), new Vector(playerLoc.x, this.topend), speed); | |
this.playerLoc = playerLoc; | |
}; | |
inherits(Bullet, Mover); | |
Bullet.prototype.reset = function() { | |
this.l.set(this.playerLoc); | |
this.d.set(this.playerLoc.x, this.topend); | |
}; | |
function Bullets(create, freq) { | |
Movers.call(this, create); | |
this.freq = freq; | |
this.counter = 0; | |
}; | |
inherits(Bullets, Movers); | |
Bullets.prototype.doUpdate = function() { | |
if (this.counter++ == this.freq) { | |
for (var i = 0; i < this.movers.length; ++i) { | |
if (this.movers[i].l.y <= 0) { // find invalid bullet | |
break; | |
} | |
}; | |
if (i == this.movers.length) { // not found | |
this.movers.push(this.create()); | |
} else { | |
this.movers[i].reset(); // reuse | |
} | |
this.counter = 0; | |
} | |
}; | |
Bullets.prototype.hitFighters = function(fighters) { // collide with fighters | |
for (var i = 0; i < this.movers.length; ++i) { | |
for (var j = 0; j < fighters.length; ++j) { | |
if (fighters[j].collide(this.movers[i])) { | |
fighters[j].hit = true; | |
this.movers[i].reset(); | |
game.score += 1000; | |
} | |
} | |
} | |
}; | |
// fighters | |
function Fighter(spt, sptEx, speed) { | |
var pos = Math.random() * theCanvas.width; | |
Mover.call(this, spt, new Vector(pos, 0), new Vector(pos, theCanvas.height), speed); | |
this.imgEx = sptEx; | |
this.exFrame = 0; | |
this.hit = false; | |
}; | |
inherits(Fighter, Mover); | |
Fighter.prototype.reset = function() { | |
var pos = Math.random() * theCanvas.width; | |
this.l.set(pos, 0); | |
this.d.set(pos, theCanvas.height); | |
this.hit = false; | |
this.exFrame = 0; | |
}; | |
Fighter.prototype.doUpdate = function() { | |
if (this.l.y == theCanvas.height) { // re-enter from top | |
this.reset(); | |
} | |
//var dir = Vector.sub(player.l, this.l); | |
//dir.limit(5); | |
//this.l.add(dir); | |
}; | |
Fighter.prototype.draw = function() { | |
if (!this.hit) { | |
this.img.draw(this.l); | |
} else { | |
if (this.exFrame == this.imgEx.length*5) { | |
this.reset(); | |
return; | |
} | |
this.imgEx[Math.floor(this.exFrame/5)].draw(this.l); | |
this.exFrame++; | |
} | |
}; | |
Fighter.prototype.collide = function(obj, accurate) { | |
if (!this.hit) { | |
return checkCollide({ | |
x: Math.round(this.l.x - this.img.width/2), | |
y: Math.round(this.l.y - this.img.height/2), | |
width: this.img.width, | |
height: this.img.height, | |
data: this.img.data, | |
}, { | |
x: Math.round(obj.l.x - obj.img.width/2), | |
y: Math.round(obj.l.y - obj.img.height/2), | |
width: obj.img.width, | |
height: obj.img.height, | |
data: obj.img.data, | |
}, accurate); | |
} else { | |
return false; | |
} | |
}; | |
function Fighters(create, n, interval) { | |
Movers.call(this, create, n, interval); | |
}; | |
inherits(Fighters, Movers); | |
Fighters.prototype.hitPlayer = function(player) { // collide with player | |
for (var i = 0; i < this.movers.length; ++i) { | |
if (this.movers[i].collide(player, true)) { | |
player.hit = true; | |
break; | |
} | |
}; | |
}; | |
// stars in background | |
function Star(spt, speed) { | |
var pos = Math.random() * theCanvas.width; | |
Mover.call(this, spt, new Vector(pos, 0), new Vector(pos, theCanvas.height), speed); | |
}; | |
inherits(Star, Mover); | |
Star.prototype.reset = function() { | |
var pos = Math.random() * theCanvas.width; | |
this.l.set(pos, 0); | |
this.d.set(pos, theCanvas.height); | |
}; | |
Star.prototype.doUpdate = function() { | |
if (this.l.y == theCanvas.height) { // re-enter from top | |
this.reset(); | |
} | |
}; | |
function Stars(create, n, interval) { | |
Movers.call(this, create, n, interval); | |
}; | |
inherits(Stars, Movers); | |
function Resources() { | |
var self = this; | |
var sprites = { | |
player: { x: 3, y: 102, w: 96, h: 122 }, | |
player_alt: { x: 168, y: 363, w: 96, h: 122 }, | |
player_ex1: { x: 168, y: 238, w: 96, h: 122 }, | |
player_ex2: { x: 333, y: 628, w: 96, h: 122 }, | |
player_ex3: { x: 333, y: 382, w: 96, h: 122 }, | |
player_ex4: { x: 432, y: 628, w: 96, h: 122 }, | |
fighter: { x: 540, y: 617, w: 48, h: 33 }, | |
fighter_ex1: { x: 273, y: 357, w: 48, h: 33 }, | |
fighter_ex2: { x: 879, y: 708, w: 48, h: 33 }, | |
fighter_ex3: { x: 267, y: 297, w: 56, h: 48 }, | |
fighter_ex4: { x: 932, y: 700, w: 56, h: 48 }, | |
bullet: { x: 1004, y: 987, w: 9, h: 20 }, | |
star: { x: 99, y: 11, w: 80, h: 80 }, | |
}; | |
var img = new Image; | |
img.src = 'striker.png'; | |
img.addEventListener('load', function() { | |
var sptPlayer = new Sprite(img, sprites.player); | |
var sptPlayerAlt = new Sprite(img, sprites.player_alt); | |
var sptPlayerEx = [new Sprite(img, sprites.player_ex1), new Sprite(img, sprites.player_ex2), new Sprite(img, sprites.player_ex3), new Sprite(img, sprites.player_ex4)]; | |
var sptFighter = new Sprite(img, sprites.fighter); | |
var sptFighterEx = [new Sprite(img, sprites.fighter_ex1), new Sprite(img, sprites.fighter_ex2), new Sprite(img, sprites.fighter_ex3), new Sprite(img, sprites.fighter_ex4)]; | |
var sptBullet = new Sprite(img, sprites.bullet); | |
var sptStar = new Sprite(img, sprites.star); | |
self.playerCreator = function(speed) { | |
return function() { | |
return new Player(sptPlayer, sptPlayerAlt, sptPlayerEx, speed); | |
}; | |
}; | |
self.bulletCreator = function(playerLoc, speed) { | |
return function() { | |
return new Bullet(sptBullet, playerLoc, speed); | |
}; | |
}; | |
self.fighterCreator = function(minSpeed, maxSpeed) { | |
return function() { | |
var speed = Math.random() * (maxSpeed - minSpeed) + minSpeed; | |
return new Fighter(sptFighter, sptFighterEx, speed); | |
}; | |
}; | |
self.starCreator = function(speed) { | |
return function() { | |
return new Star(sptStar, speed); | |
}; | |
}; | |
game.state = 'start'; | |
}); | |
}; | |
var theCanvas = document.getElementById('canvas'); | |
var context = theCanvas.getContext('2d'); | |
var clientRect = theCanvas.getBoundingClientRect(); | |
var game = { | |
state: '', // start, running, over | |
score: 0, | |
}; | |
var options = { | |
fps: 60, | |
bgColor: 'rgb(189, 198, 197)', | |
startLoc: new Vector(theCanvas.width/2, theCanvas.height - 100), | |
playerSpeed: 30, | |
bulletSpeed: 20, | |
bulletFreq: 10, // higher is slower | |
fighterMinSpeed: 2, | |
fighterMaxSpeed: 4, | |
fighterNum: 10, | |
fighterFreq: 1000, // 1s for show interval | |
starSpeed: 0.5, | |
starNum: 5, | |
starFreq: 5000, // 5s | |
}; | |
var mouse = new Vector(options.startLoc); | |
theCanvas.addEventListener('mousemove', function(e) { | |
mouse.set(e.clientX - clientRect.left, e.clientY - clientRect.top); | |
}); | |
var res = new Resources; | |
var player; | |
var bullets; | |
var fighters; | |
var stars; | |
var message; | |
var textWidth; | |
(function gameLoop() { | |
setTimeout(gameLoop, 1000/options.fps); | |
context.fillStyle = options.bgColor; | |
context.fillRect(0, 0, theCanvas.width, theCanvas.height); | |
context.fillStyle = '#000'; | |
switch(game.state) { | |
case 'start': | |
player = res.playerCreator(options.playerSpeed)(); | |
bullets = new Bullets(res.bulletCreator(player.l, options.bulletSpeed), options.bulletFreq); | |
fighters = new Fighters(res.fighterCreator(options.fighterMinSpeed, options.fighterMaxSpeed), options.fighterNum, options.fighterFreq); | |
stars = new Stars(res.starCreator(options.starSpeed), options.starNum, options.starFreq); | |
context.font = '50px Arial'; | |
message = 'GAME OVER'; | |
textWidth = context.measureText(message).width; | |
game.state = 'running'; | |
break; | |
case 'running': | |
stars.update(); | |
stars.draw(); | |
context.fillText(game.score, 10, 50); | |
player.update(); | |
player.draw(); | |
if (!player.hit) { | |
bullets.update(); | |
bullets.draw(); | |
} | |
fighters.update(); | |
fighters.draw(); | |
fighters.hitPlayer(player); | |
bullets.hitFighters(fighters.movers); | |
break; | |
case 'over': | |
stars.update(); | |
stars.draw(); | |
context.fillText(game.score, 10, 50); | |
context.fillText(message, (theCanvas.width/2) - (textWidth/2), theCanvas.height/2); | |
break; | |
} | |
})(); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment