Here's a little game written in HTML5 using bleeding edge audio. Since I don't do much HTML5 or Javascript, I'm particularly interested in:
- Structure Is this a reasonable way to structure the Javascript? Should I encapsulate everything in an object?
- Compatibility I believe the older versions of Internet Explorer won't work with this. Are there other issues with modern browsers?
The in-game instructions are terse, but the keys j, k, d and f correspond to upper left, upper right, lower left and lower right quadrants respectively. Alternatively, you can use the mouse. It may also work with a touchscreen -- I'd be interested in learning if it does. It works with at least some touchscreens (Windows 8 and iPhone), but I'd be interested in learning of touchscreens where it does not seem to work.
var litcolors = ['red', 'yellow', 'lightblue', 'lightgreen'];
var colors = ['darkred', 'darkorange', 'darkblue', 'darkgreen'];
var ctx, innerRadius, outerRadius;
var states = [ false, false, false, false ];
var keys = [ 74, 75, 70, 68 ];
var notes = [ 261.626, 329.628, 391.995, 523.251 ];
var sequence = [];
var seqitem = 0;
var c;
var turn = 0;
var timeout;
function instructions() {
alert("After the computer shows a sequence,\ntry to reproduce it.\nYou can use the keys kldf or the mouse");
}
function init() {
c = document.getElementById('simon');
ctx = c.getContext("2d");
paintToy(-1);
instructions();
c.addEventListener('mousedown', function(evt) {
var mousePos = getMousePos(c, evt);
var quadrant = getQuadrant(mousePos);
if (turn == 1) {
tryMove(quadrant);
}
}, false);
c.addEventListener('mouseup', function(evt) {
paintToy(-1);
}, false);
document.addEventListener('keyup', function(evt) {
paintToy(-1);
}, false);
document.addEventListener('keydown', function(evt) {
var quadrant = getQuadrantKey(evt.keyCode);
if (turn == 1) {
tryMove(quadrant);
}
}, false);
computerTurn();
}
// Temporary workaround until AudioContext is standardized
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var audioCtx = new AudioContext();
var osc = audioCtx.createOscillator();
osc.type = 'triangle';
osc.start(0);
var score = 0;
function beep(frequency, duration, after) {
if (typeof after != "function") {
after = function () {};
}
osc.frequency.value = +frequency;
osc.connect(audioCtx.destination);
setTimeout(function () {
osc.disconnect();
after();
}, +duration);
}
function paintToy(litquadrant) {
writeMessage(c,10,25, ""+score);
writeMessage(c,400,490, "Simon");
outerRadius = c.width * 0.45;
innerRadius = c.width * 0.05;
ctx.translate(c.width / 2, c.height / 2);
var i;
for (i = 0; i < 4; i++) {
states[i] = (litquadrant == i);
slice(i);
}
ctx.translate(-c.width / 2, -c.height / 2);
}
function slice(quadrant) {
ctx.rotate(quadrant * Math.PI/2);
ctx.beginPath();
ctx.arc(-5,-5,innerRadius,Math.PI, 3*Math.PI/2 );
ctx.lineTo(-5,-outerRadius-5);
ctx.arc(-5,-5,outerRadius, 3*Math.PI/2,Math.PI, true);
ctx.closePath();
if (states[quadrant]) {
ctx.fillStyle = litcolors[quadrant];
} else {
ctx.fillStyle = colors[quadrant];
}
ctx.fill();
ctx.strokeStyle = 'black';
ctx.stroke();
ctx.rotate(-quadrant * Math.PI/2);
}
function getQuadrant(mousePos) {
var quadrant = 0;
if (mousePos.y > c.width/2) {
quadrant = 3;
}
if (mousePos.x > c.width/2) {
if (quadrant == 3) {
quadrant = 2;
} else {
quadrant++;
}
}
return quadrant;
}
function writeMessage(canvas, x, y, message) {
var context = canvas.getContext('2d');
context.clearRect(x, y-30, canvas.width, 200);
context.font = '18pt Calibri';
context.fillStyle = 'black';
context.fillText(message, x, y);
}
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: Math.round((evt.clientX-rect.left)/(rect.right-rect.left)*canvas.width),
y: Math.round((evt.clientY-rect.top)/(rect.bottom-rect.top)*canvas.height)
};
}
function getQuadrantKey(keycode) {
var i;
for (i = 0; i < 4; i++) {
if (keys[i] == keycode) {
return i;
}
}
return -1;
}
function sound(quadrant) {
if ((quadrant < 0) || (quadrant > 3))
return;
beep(notes[quadrant],200, false);
}
function tryMove(quadrant) {
paintToy(quadrant);
if (sequence[seqitem++] == quadrant) {
sound(quadrant);
clearTimeout(timeout);
if (seqitem == sequence.length) {
score++;
turn = 0;
setTimeout(computerTurn, 1000);
} else {
timeout = setTimeout(lose, 5000);
}
} else {
lose();
}
}
function computerPlay() {
sound(sequence[seqitem]);
paintToy(sequence[seqitem++]);
setTimeout(clear, 200);
if (seqitem < sequence.length) {
setTimeout(computerPlay, 1000);
} else {
turn = 1;
humanTurn();
}
}
function computerTurn() {
sequence.push(Math.floor((Math.random() * 4)));
seqitem = 0;
computerPlay();
}
function clear() {
beep(0, 200, false);
paintToy(-1);
}
function humanTurn() {
seqitem = 0;
timeout = setTimeout(lose, 5000);
}
function lose() {
turn = -1;
clearTimeout(timeout);
beep(155.563, 200, function(){alert("Sorry, you lose!")});
}
function losemsg() {
alert("Sorry, you lose!");
}
init();
<!DOCTYPE html>
<html>
<head>
<title>Simon</title>
</head>
<body>
<canvas id="simon" width="500" height="500"
style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag. How sad.</canvas>
</body>
</html>