My entry for the js1k competition. 1012 bytes of JavaScript. Arrow keys move, space fires.
As per the competition rules, this won't work in IE, except perhaps version 9 (untested). The game gradually speeds up, so there's a degree of difficulty in getting a high score.
Feedback: joeri (at) sebrechts.net
Version: 2.0 (link to v1.0 / changes: retro look, larger field, better bullet timing)
View source for minified source code. Documented source code:m = Math; // k = keyboard state object, reuse Math.abs to avoid an initializer // so k() = Math.abs(), but k[keycode] = down state k=m.abs; // s = score // q = quit, if 1 then game over // g = rate counter to generate enemy fighters g = s = q = 0; r = 53; // generate a fighter every this many frames, 1 frame per 40 ms // key state tracking this.onkeydown = this.onkeyup = function(e) { // which is shorter than keyCode k[e.which] = (e.type == 'keydown'); if (k[32] & !q) { // d: inverted x direction travelling speed f.push({x: p.x, y: p.y, s: 4, d: -3, b: 1}); }; }; with(document.getElementById('c')) { style.border = 'inset #999'; width = w = 350; height = h = 150; c = getContext('2d'); // fighters // s: delta x for drawing p = {x: 10, y: h/2, s: 8}; // player 1 f = []; // enemy fighters and bullets, bullets have b: 1 with (f) { // d = draw item // p.s = scale factor for flipping rendering across the x axis function d(p) { c.beginPath(); c.moveTo(p.x, p.y); x=p.x-p.s; v = p.b?1:5; c.lineTo(x, p.y+v); c.lineTo(x, p.y-v); c.fill(); } setInterval(function() { l=length; // update the position of the main fighter p.x += k[39]?2:0; // right p.x -= k[37]?2:0; // left p.y -= k[38]?2:0; // up p.y += k[40]?2:0; // down // should we generate an enemy fighter and enemy bullets? // value is inverted: g=0 -> generate fighter g %= m.max(10, r-=0.02)|0; // slowly increase the rate of fighters // generate enemy fighter if (!g++) push({x: w, y: h*m.random(), s: -8, d: 1}); // draw everything t = c.fillStyle = c.createLinearGradient(0,0,w,h); t.addColorStop(0,'#170'); t.addColorStop(1,'#010'); c.fillRect(0,0,w,h); c.fillStyle = '#3F1'; i=l; while (i--) { // animate enemy fighters and bullets x = (o=f[i]).x -= o.d; // if this is a player 1 bullet, // do collision detection against enemy fighters // o.d < 0: player 1 object if (o.b & o.d<0) { // for every enemy fighter j=l; while (j--) { z=f[j]; // if collision, remove bullet and fighter and increase score // !z.b = not a bullet if (z && !z.b && k(x-z.x-4)<4 && k(z.y-o.y)<4) { if (i>j) i--; splice(j, 1); splice(i, 1); s++; }; }; // end while (j--) }; // do collision detection between player 1 and enemy objects // o.d>0 : enemy object if (!q && o.d>0 && k(p.x-x-6)<4 && k(o.y-p.y)<5) { q=1; splice(i, 1); }; // if this is a fighter, and not a bullet, // and it's time to fire bullets, then fire a bullet // x%h -> fire bullets every h pixels if (!(o.b | x%h)) push({x: x, y: o.y, s: -4, d: 2, b: 1}); // remove them if they go out of the screen if (k(x) > w+9) splice(i, 1); // draw all items d(o); }; // end while (i--) if (!q) d(p); c.fillText(s, 2, h-2); }, 40); } }