Skip to content

Instantly share code, notes, and snippets.

@omaraboumrad
Last active December 25, 2015 23:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save omaraboumrad/7057456 to your computer and use it in GitHub Desktop.
Save omaraboumrad/7057456 to your computer and use it in GitHub Desktop.
space shooter
p{
font-family: verdana;
font-size: 12px;
}
<p>[A/D] Move [Left/Right] Rotate [Space] Shoot [Ctrl] Super Weapon</p>
window.onload = init;
function init() {
var world = {
sound: false,
scene: {
width: 400,
height: 600,
url: 'https://dl.dropboxusercontent.com/u/18982788/projectile/bg.jpg',
background: '#FFFFFF',
foreground: '#000000',
stroke: { color:'#000000', thickness: 4 } // Border
},
info: { // Info bar
height: 50,
background: '#000000',
foreground: '#FFFF00',
},
game:{
turret: {
url: 'https://dl.dropboxusercontent.com/u/18982788/projectile/turret.png',
nuclear_url: 'https://dl.dropboxusercontent.com/u/18982788/projectile/nuclear.png',
color: '#FF0000',
length: 40,
thickness:8,
frequency: 300, // of fire
step: 2, // turn steps
angle: 0, // starting angle
bullet: {
url: 'https://dl.dropboxusercontent.com/u/18982788/projectile/shot.png',
super_url:'https://dl.dropboxusercontent.com/u/18982788/projectile/super.png',
color: '#000000',
width: 4,
speed: 10, // travel speed
}
},
enemies: {
url: 'https://dl.dropboxusercontent.com/u/18982788/projectile/enemy.jpg',
explosion: 'https://dl.dropboxusercontent.com/u/18982788/projectile/explosion.png',
color: '#0000FF',
frequency: 500, // of spawn
basespeed: 0.5,
levelmodifier: 0.4,
count: 10, // per level
},
elapsed: {
shot: 0,
spawn: 0,
},
score: {
level: 1,
lives: 3,
super: 2,
current: 0,
best: 0,
}
}
};
var intersect = function(r1, r2) {
return !(r2[0] > r1[1] ||
r2[1] < r1[0] ||
r2[2] > r1[3] ||
r2[3] < r1[2]);
};
var sprite = {
x: world.scene.width/2,
y: world.scene.height-10,
projectiles: [],
enemies: [],
spawned: 0,
pressed: {},
loaded : 0,
bg: { elapsed: 0, angle:0 },
images: {},
nuke: {
playing:false,
alpha: 0,
elapsed:0,
total:0
},
sounds:{
shoot: new Audio('https://dl.dropboxusercontent.com/u/18982788/projectile/shoot.wav')
},
reset: function(){
world.game.score.current = 0;
world.game.score.level = 1;
world.game.score.lives = 3;
world.game.score.super = 2;
world.game.elapsed.shot = 0;
world.game.elapsed.spawn = 0;
this.enemies = [];
this.spawned = 0 ;
},
right: function() { this.handle_angle(-1); },
left: function() { this.handle_angle(1); },
handle_angle: function(sign){
world.game.turret.angle += world.game.turret.step*sign;
if(world.game.turret.angle > 90) world.game.turret.angle = 90;
else if(world.game.turret.angle < -90) world.game.turret.angle = -90;
},
move_right: function() { this.handle_move(1); },
move_left: function() { this.handle_move(-1); },
handle_move: function(sign){
this.x += 5*sign;
if(this.x - world.game.turret.length < 0) this.x = world.game.turret.length;
else if(this.x + world.game.turret.length > world.scene.width) this.x = world.scene.width - world.game.turret.length;
},
render_projectiles: function(){
for(var i=0;i<this.projectiles.length;i++){
var projectile = this.projectiles[i];
var _angle = projectile[2] * Math.PI / 180 - Math.PI/2;
projectile[0] += world.game.turret.bullet.speed * Math.cos(_angle);
projectile[1] += world.game.turret.bullet.speed * Math.sin(_angle);
if(projectile[0] < 0
|| projectile[0] > world.scene.width
|| projectile[1] < 0){
this.projectiles.splice(i,1);
i--;
}else{
ctx.drawImage(
this.images.bullet,
projectile[0] - this.images.bullet.width/2,
projectile[1] - this.images.bullet.height/2
);
}
}
},
render_enemies: function(){
for(var i=0;i<this.enemies.length;i++){
var enemy = this.enemies[i];
enemy[1] += enemy[2];
if(enemy[1] > world.scene.height - world.scene.width/20){
this.enemies.splice(i,1);
i--;
if(world.game.score.current > world.game.score.best)
world.game.score.best = world.game.score.current;
world.game.score.lives -= 1;
if(world.game.score.lives === 0) this.reset();
}else{
ctx.drawImage(
this.images.enemies,
enemy[0]-this.images.enemies.width/2,
enemy[1]-this.images.enemies.height/2);
}
}
},
render: function(){
// Turret
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(world.game.turret.angle * Math.PI/180);
ctx.drawImage(
this.images.turret,
-this.images.turret.width/2,
-this.images.turret.height
);
ctx.restore();
},
shoot: function(){
if(world.game.elapsed.shot > world.game.turret.frequency){
var _angle = world.game.turret.angle * Math.PI / 180 - Math.PI/2;
this.projectiles.push([
this.x + this.images.turret.height * Math.cos(_angle),
this.y + this.images.turret.height* Math.sin(_angle),
world.game.turret.angle
]);
world.game.elapsed.shot = 0;
if(world.sound){
this.sounds.shoot.currentTime = 0;
this.sounds.shoot.play();
}
}
},
fire_super: function(){
if(world.game.score.super > 0){
this.enemies = [];
world.game.score.super -= 1;
this.pressed[17] = false;
this.nuke.playing = true;
}
},
spawn: function(){
if(this.enemies.length < world.game.enemies.count && this.spawned != world.game.enemies.count){
x = Math.floor((world.scene.width - this.images.enemies.width) * Math.random()) + 5 + this.images.enemies.width/2;
v = world.game.enemies.basespeed + world.game.score.level * world.game.enemies.levelmodifier ;
this.enemies.push([x, 10, v]);
this.spawned += 1;
}
if(this.spawned == world.game.enemies.count && this.enemies.length == 0){
world.game.score.level += 1;
this.spawned = 0;
}
},
handle_collision: function(){
for(var i=0;i<this.enemies.length;i++){
var e = this.enemies[i];
var r1 = [
e[0]-this.images.enemies.width/2,
e[0]+this.images.enemies.width/2,
e[1]-this.images.enemies.height/2,
e[1]+this.images.enemies.height/2];
for(var j=0;j<this.projectiles.length;j++){
var p = this.projectiles[j];
var r2 = [p[0], p[0]+world.game.turret.bullet.width, p[1], p[1]+world.game.turret.bullet.width];
if(intersect(r1, r2)){
explosions.push(new Explosion(e[0], e[1], this.images.explosion, partitions, false));
world.game.score.current += world.game.score.level;
this.projectiles.splice(j, 1);
this.enemies.splice(i, 1);
i--;
break;
}
}
}
},
handle_events: function(){
if(this.pressed[37]) this.right();
if(this.pressed[39]) this.left();
if(this.pressed[32]) this.shoot();
if(this.pressed[65]) this.move_left();
if(this.pressed[68]) this.move_right();
if(this.pressed[17]) this.fire_super();
},
handle_elapsed: function(dt){
world.game.elapsed.spawn += dt;
world.game.elapsed.shot += dt;
if(world.game.elapsed.spawn > world.game.enemies.frequency){
this.spawn();
world.game.elapsed.spawn = 0;
}
if(this.nuke.playing){
this.nuke.elapsed += dt;
this.nuke.total += dt;
var sign = 1;
if(this.nuke.total > 500) sign = -1;
if(this.nuke.elapsed > 100){
this.nuke.alpha += 0.2*sign;
this.nuke.elapsed = 0;
}
ctx.globalAlpha = this.nuke.alpha;
ctx.drawImage(
this.images.nuclear,
world.scene.width/2 - this.images.nuclear.width/2,
world.scene.height/2 - this.images.nuclear.height/2
);
ctx.globalAlpha = 1;
if(this.nuke.total > 1000){
this.nuke.playing = false;
this.nuke.elapsed = 0;
this.nuke.total = 0;
this.nuke.alpha = 0;
}
}
},
render_explosions: function(dt){
for(var i=0; i<explosions.length; i++){
if(explosions[i].render(dt)){
explosions.splice(i, 1);
i--;
}
}
}
};
var canvas = document.createElement('canvas');
document.body.appendChild(canvas);
canvas.width = world.scene.width;
canvas.height = world.scene.height + world.info.height;
var ctx = canvas.getContext('2d');
var draw_parallax = function(data){
sprite.bg.elapsed += data.dt;
var _angle = sprite.bg.angle * Math.PI / 180 - Math.PI/2;
x = Math.cos(_angle)*world.scene.width/2 - sprite.images.background.width/2 + world.scene.width/2;
y = Math.sin(_angle)*world.scene.height/2 - sprite.images.background.height/2 + world.scene.height/2;
ctx.drawImage(sprite.images.background, x, y);
if(sprite.bg.elapsed>10){
sprite.bg.angle+=0.1;
if(sprite.bg.angle > 360) sprite.bg.angle = 0;
sprite.bg.elapsed = 0;
}
};
var draw_world = function(data){
ctx.fillStyle = world.scene.background;
ctx.fillRect(0,0, world.scene.width, world.scene.height);
ctx.strokeStyle = world.scene.stroke.color;
ctx.lineWidth = world.scene.stroke.thickness;
// Parallax
draw_parallax(data);
// End Parallax
ctx.strokeRect(2, 2, world.scene.width-4, world.scene.height-4);
ctx.fillStyle = world.info.background;
ctx.fillRect(0, world.scene.height, world.scene.width, world.info.height);
ctx.fillStyle = world.info.foreground;
ctx.font = '12pt verdana bold';
ctx.fillText('[S] ' + world.game.score.current, 60, world.scene.height + 20);
ctx.fillText('[T] ' + world.game.score.best, 60, world.scene.height + 40);
ctx.fillText('[L] ' + world.game.score.level, 10, world.scene.height + 20);
ctx.fillText('[H] ', 150, world.scene.height + 20);
ctx.fillText("Sound: " + (world.sound?"On":"Off") , 150, world.scene.height + 40)
ctx.fillStyle = world.game.turret.color;
for(var i=0;i<world.game.score.lives;i++){
ctx.fillRect( 185 + i*22, world.scene.height + 8 , 15, 15);
}
ctx.fillStyle = world.game.enemies.color;
for(var i=0;i<world.game.score.super;i++){
ctx.drawImage(sprite.images.super, 285 + i*30, world.scene.height + 8);
}
};
document.addEventListener('keydown', function(evt){
sprite.pressed[evt.keyCode] = true;
}, true);
document.addEventListener('keyup', function(evt){
sprite.pressed[evt.keyCode] = false;
}, true);
window.requestAnimFrame = function(){
return (
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(/* function */ callback){
window.setTimeout(callback, 1000 / 60);
}
);
}();
var Explosion = function(x, y, sheet, partitions, repeat){
this.x = x;
this.y = y;
this.at = 0;
this.elapsed = 0;
this.render = function(dt){
this.elapsed += dt;
if(this.elapsed > 50){
this.elapsed = 0;
this.at += 1;
if(this.at == partitions.length){
if(repeat){
this.at = 0;
}else{
return true;
}
}
}
var offset_x = partitions[this.at][0];
var offset_y = partitions[this.at][1];
var dw = partitions[this.at][2];
var dh = partitions[this.at][3];
ctx.drawImage(
sheet,
offset_x, offset_y,
dw, dh,
this.x - dw/2 , this.y - dh/2,
dw, dh
);
return false;
};
};
var start = new Date();
var last = new Date();
var explosions = [];
var partitions = [
[0, 9, 8, 8],
[15, 6, 14, 14],
[33, 0, 25, 25],
[61, 2, 23, 22],
[86, 3, 21, 20],
[110, 5, 17, 17],
[130, 7, 14, 14],
[147, 9, 11, 10],
[161, 10, 8, 8],
[173, 11, 7, 7]
];
function loop() {
setTimeout(function(){
window.requestAnimFrame(loop);
var now = new Date();
var dt = now - last;
var total = (now - start) / 1000;
last = now;
draw_world({ total: total, dt: dt });
sprite.render();
sprite.render_projectiles();
sprite.render_enemies();
sprite.render_explosions(dt);
sprite.handle_elapsed(dt);
sprite.handle_collision();
sprite.handle_events();
}, 1000/60);
}
var ImageLoader = function(args, callback){
var self = this;
this.loaded = 0;
this.images = {};
for(var entry in args){
var _img = new Image();
this.images[entry] = _img;
_img.onload = function() {
self.loaded +=1;
if(self.loaded == Object.keys(args).length)
callback(self.images);
}
_img.src = args[entry];
}
};
image_map = {
'turret': world.game.turret.url,
'bullet': world.game.turret.bullet.url,
'scene': world.scene.url,
'enemies': world.game.enemies.url,
'explosion': world.game.enemies.explosion,
'nuclear_icon': world.game.turret.bullet.super_url,
'nuclear': world.game.turret.nuclear_url
};
var loader = new ImageLoader(image_map, function(images){
sprite.images.background = images.scene;
sprite.images.turret = images.turret;
sprite.images.bullet = images.bullet;
sprite.images.enemies = images.enemies;
sprite.images.super = images.nuclear_icon;
sprite.images.nuclear = images.nuclear;
sprite.images.explosion = images.explosion;
loop();
});
}
@omaraboumrad
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment