123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520 |
- function SoundSafari(beatInfo) {
- var canvas = Game.canvas;
- var ctx = Game.context;
- // Constants
- var RADIUS = 8;
- var LEAF_SIZE = 8;
- var LEVEL_SIZE = 800;
- var ALLOWANCE = (LEVEL_SIZE - canvas.width)/2;
- var METER_WIDTH = 120;
- var METER_HEIGHT = 40;
- var METER_SPEED = 10;
- var PLAYER_SPEED = 50;
- var STAGE_SPEED = 25;
- var RIPPLE_SIZE = 10;
- var RIPPLE_SPEED = 10;
- var DOT_SIZE = 3;
- var DOT_DISTANCE = 4 * 5;
- var starColors = [
- 'maroon',
- 'red',
- 'orange',
- 'yellow',
- 'olive',
- 'green',
- 'teal',
- 'blue',
- 'navy',
- 'purple'
- ];
- var debug = false;
- var state = StateMachine.create({
- initial: 'loading',
- events: [
- {name: 'ready', from: 'loading', to: 'waiting'},
- {name: 'play', from: 'waiting', to: 'playing'},
- {name: 'point', from: 'playing', to: 'waiting'}
- ],
- callbacks: {
- onready: function() {
- reset();
- },
- onpoint: function() {
- ++points;
- soundManager.play('get');
- switchBeat(points % beats.length);
- }
- }
- });
- var gameTime = 0;
- var beats = [];
- var currentBeats = [];
- // Drawing functions
- {
- function drawCircle(point, r, w, color) {
- ctx.beginPath();
- ctx.arc(point.x, point.y, r, 0, 2*Math.PI, false);
- ctx.lineWidth = w;
- ctx.strokeStyle = color;
- ctx.stroke();
- }
- function drawLeafV(point, h, w) {
- ctx.beginPath();
- ctx.moveTo(point.x, point.y);
- ctx.quadraticCurveTo(point.x + w, point.y + h/2, point.x, point.y + h);
- ctx.quadraticCurveTo(point.x - w, point.y + h/2, point.x, point.y);
- ctx.fill();
- }
- function drawLeafH(point, w, h) {
- ctx.beginPath();
- ctx.moveTo(point.x, point.y);
- ctx.quadraticCurveTo(point.x + w/2, point.y + h, point.x + w, point.y);
- ctx.quadraticCurveTo(point.x + w/2, point.y - h, point.x, point.y);
- ctx.fill();
- }
- function drawPlayer(point) {
- drawCircle(point, RADIUS, 5, "#FFFFFF");
- }
- function drawSpectrum() {
- var barHeight = 30;
- var barWidth = 6;
- var barSpacing = 2;
- var margin = 5;
- ctx.strokeStyle = 'white';
- ctx.lineWidth = 2;
- ctx.beginPath();
- ctx.rect(
- margin - ctx.lineWidth,
- canvas.height - barHeight - ctx.lineWidth - margin,
- activeTarget.spectrum.length * (barWidth+barSpacing) + 2 * ctx.lineWidth - barSpacing,
- barHeight + 2 * ctx.lineWidth
- );
- ctx.stroke();
- for(var i = 0; i < activeTarget.spectrum.length; ++i) {
- var height = activeTarget.spectrum[i]*20;
- ctx.fillStyle = starColors[i];
- ctx.fillRect(i * (barWidth + barSpacing) + margin, canvas.height - height - margin, barWidth, height);
- }
- }
- }
- // Helpers
- {
- function distX(a, b) {
- var dx = a.x - b.x;
- if(dx > LEVEL_SIZE / 2) dx -= LEVEL_SIZE;
- if(dx < -LEVEL_SIZE / 2) dx += LEVEL_SIZE;
- return dx;
- }
- function distSq(a, b) {
- var c = distX(a, b);
- var d = a.y - b.y;
- return c * c + d * d;
- }
- function colliding(a, b) {
- return distSq(a, b) <= 4 * RADIUS * RADIUS;
- }
- function translatePoints(point) {
- var x = point.x - player.x + canvas.width/2;
- if(x < -ALLOWANCE) x += LEVEL_SIZE;
- if(x > LEVEL_SIZE - ALLOWANCE) x -= LEVEL_SIZE;
- return {x:x, y:point.y};
- }
- function nextPlayTime(duration) {
- return Math.ceil(gameTime / duration) * duration;
- }
- }
- // "Entities"
- {
- function Meter() {
- this.offset = 10;
- this.amp = 0;
- this.percentage = 0;
- this.direction = METER_SPEED;
- }
- Meter.prototype.draw = function() {
- var left = (canvas.width - METER_WIDTH) / 2;
- var vmid = this.offset + METER_HEIGHT / 2;
- var third = METER_WIDTH / 3;
- var peak = METER_HEIGHT / 2;
- ctx.fillStyle = 'black';
- ctx.rect(left, this.offset, METER_WIDTH, METER_HEIGHT);
- ctx.fill();
- var cpUp = lerp(vmid, vmid + peak * this.amp, this.percentage);
- var cpDown = lerp(vmid, vmid - peak * this.amp, this.percentage);
- ctx.lineWidth = 2;
- ctx.strokeStyle = 'red';
- ctx.beginPath();
- var pos = left;
- ctx.moveTo(left, vmid);
- ctx.quadraticCurveTo(pos+third/2, cpUp, pos+third, vmid);
- pos += third;
- ctx.quadraticCurveTo(pos+third/2, cpDown, pos+third, vmid);
- pos += third;
- ctx.quadraticCurveTo(pos+third/2, cpUp, pos+third, vmid);
- ctx.stroke();
- ctx.lineWidth = 3;
- ctx.strokeStyle = 'white';
- ctx.beginPath();
- ctx.rect(left, this.offset, METER_WIDTH, METER_HEIGHT);
- ctx.stroke();
- };
- Meter.prototype.onUpdate = function(dt) {
- if(this.percentage > 1) {
- this.direction = -METER_SPEED;
- }
- if(this.percentage < -1) {
- this.direction = METER_SPEED;
- }
- this.percentage += this.direction * dt;
- this.amp = 0.5 + lerp(0, 1, activeTarget.shimmerFactor);
- };
- function Star(x, y) {
- this.x = x;
- this.y = y;
- this.alpha = 0;
- this.randomize();
- }
- Star.prototype.randomize = function() {
- this.type = Math.floor(Math.random()*starColors.length);
- this.color = starColors[this.type];
- };
- Star.prototype.draw = function() {
- ctx.fillStyle = this.color;
- ctx.globalAlpha = this.alpha;
- var adjPosition = translatePoints(this);
- ctx.fillRect(adjPosition.x, adjPosition.y, DOT_SIZE, DOT_SIZE);
- var top = {x: adjPosition.x + DOT_SIZE/2, y: adjPosition.y - DOT_SIZE/2 };
- var bottom = {x: adjPosition.x + DOT_SIZE/2, y: adjPosition.y + DOT_SIZE/2*3};
- var left = {x: adjPosition.x - DOT_SIZE/2, y: adjPosition.y + DOT_SIZE/2 };
- var right = {x: adjPosition.x + DOT_SIZE/2*3, y: adjPosition.y + DOT_SIZE/2 };
- drawLeafH(left, -LEAF_SIZE, LEAF_SIZE/2);
- drawLeafH(right, LEAF_SIZE, LEAF_SIZE/2);
- drawLeafV(top, -LEAF_SIZE, LEAF_SIZE/2);
- drawLeafV(bottom, LEAF_SIZE, LEAF_SIZE/2);
- ctx.globalAlpha = 1;
- };
- Star.prototype.onUpdate = function() {
- if(this.y > canvas.height + DOT_DISTANCE * 2) {
- this.y -= canvas.height + DOT_DISTANCE * 4;
- this.randomize();
- }
- if(state.current == 'playing') {
- var newAlpha = 1.5 * activeTarget.spectrum[this.type];
- var a = 0.9;
- this.alpha = Math.min(1, activeTarget.shimmerFactor * (this.alpha * (1-a) + newAlpha * a));
- }
- else if(state.current == 'waiting') {
- this.alpha = this.alpha * 0.95;
- }
- };
- function Dot(x, y) {
- this.x = x;
- this.y = y;
- }
- Dot.prototype.draw = function() {
- ctx.fillStyle = 'gray';
- var adjPosition = translatePoints(this);
- ctx.fillRect(adjPosition.x, adjPosition.y, DOT_SIZE, DOT_SIZE);
- };
- Dot.prototype.onUpdate = function() {
- if(this.y > canvas.height + DOT_DISTANCE * 2) {
- this.y -= canvas.height + DOT_DISTANCE * 4;
- }
- };
- function Target(beat) {
- this.beat = beat;
- this.alpha = 0;
- this.spectrum = [];
- if(points < 4) {
- this.alpha = ease(1-points/4);
- }
- this.rippleCounter = 0;
- this.reset();
- }
- Target.prototype.reset = function() {
- this.y = -20;
- this.x = Math.random()*LEVEL_SIZE;
- };
- Target.prototype.color = function(p) {
- return color(0, 255, 0, p*this.alpha);
- };
- Target.prototype.draw = function() {
- var adjPosition = translatePoints(this);
- drawCircle(adjPosition, RADIUS, 3, this.color(1));
- drawCircle(adjPosition, 2, 3, this.color(1));
- // ripples
- var p = lerp(1, 0, ease(this.rippleCounter/RIPPLE_SIZE));
- drawCircle(adjPosition, RADIUS+7+this.rippleCounter, 1, this.color(p));
- drawCircle(adjPosition, RADIUS+4+this.rippleCounter, 1, this.color(p*0.8));
- drawCircle(adjPosition, RADIUS+1+this.rippleCounter, 1, this.color(p*0.5));
- };
- Target.prototype.onUpdate = function(dt) {
- this.rippleCounter += RIPPLE_SPEED * dt;
- if(this.rippleCounter > RIPPLE_SIZE) {
- this.rippleCounter = 0;
- }
- if(colliding(player, this)) {
- state.point();
- this.beat.sound.setPan(0);
- this.beat.sound.setVolume(100);
- this.shouldDelete = true;
- return;
- }
- if(this.y > canvas.height + 20) {
- this.reset();
- }
- var xDist = distX(this, player);
- var s = sign(xDist);
- var xDistAbs = easeOutExpo(Math.abs(xDist), 0, 100, LEVEL_SIZE/2);
- var yDist = this.y - player.y;
- var angle = 0;
- if(yDist != 0) {
- angle = easeOutExpo(Math.abs(Math.atan2(Math.abs(yDist), xDist) - Math.PI/2), 0, 100, Math.PI);
- }
- else {
- angle = s * 100;
- }
- this.beat.sound.setPan(Math.max(xDistAbs, angle) * s);
- var rDist = xDist*xDist + yDist*yDist;
- if(rDist >= 250000) {
- rDist = 250000;
- }
- rDist = 250000 - rDist;
- rDist /= 2500;
- this.beat.sound.setVolume(rDist);
- this.shimmerFactor = easeOutExpo(Math.abs(xDist), 1, -1, LEVEL_SIZE/2) * rDist / 100;
- if(isNaN(this.shimmerFactor) || this.shimmerFactor < 0) {
- this.shimmerFactor = 0;
- }
- var pos = gameTime % this.beat.info.duration;
- if(pos < this.beat.sound.duration) this.spectrum = this.beat.sound.getSpectrum(pos);
- };
- }
- var player = {
- x: 0,
- y: canvas.height - 20,
- dx: PLAYER_SPEED
- };
- var meter = new Meter();
- var points;
- var entities;
- var activeTarget;
- function switchBeat(index) {
- var currentBeat = beats[index];
- currentBeats.push(currentBeat);
- if(currentBeats.length > 4) {
- currentBeats.splice(0, 1);
- }
- activeTarget = new Target(currentBeat);
- entities.push(activeTarget);
- }
- function reset() {
- points = 0;
- entities = [];
- for(var j = 0; j < canvas.height / DOT_DISTANCE + 4; ++j) {
- for(var i = 0; i < LEVEL_SIZE / DOT_DISTANCE; ++i) {
- var x = i * DOT_DISTANCE;
- var y = (j-4) * DOT_DISTANCE;
- if(j % 2 == 0 && i % 2 == 0) {
- entities.push(new Dot(x, y));
- }
- if(j % 4 == 1) {
- if(i % 4 == 0) {
- entities.push(new Star(x, y));
- }
- else {
- entities.push(new Dot(x, y));
- }
- }
- if(j % 4 == 3) {
- if(i % 4 == 2) {
- entities.push(new Star(x, y));
- }
- else {
- entities.push(new Dot(x, y));
- }
- }
- }
- }
- switchBeat(0);
- }
- // Update
- this.update = function(dt) {
- if(state.current == 'loading') return;
- var delta = dt * 1000;
- currentBeats.map(function(beat) {
- if(gameTime + delta > nextPlayTime(beat.info.duration)) {
- if(beat == activeTarget.beat && state.current == 'waiting') {
- state.play();
- }
- beat.sound.play();
- }
- });
- if(37 in Game.keysDown) {
- player.x -= player.dx * dt;
- }
- if(39 in Game.keysDown) {
- player.x += player.dx * dt;
- }
- player.x = (player.x + LEVEL_SIZE) % LEVEL_SIZE;
- for(var i = entities.length - 1; i >= 0; --i) {
- var entity = entities[i];
- entity.y += STAGE_SPEED * dt;
- entity.onUpdate(dt);
- if(entity.shouldDelete) {
- entities.splice(i, 1);
- }
- }
- meter.onUpdate(dt);
- gameTime += delta;
- };
- this.draw = function() {
- if(state.current != 'loading') {
- ctx.fillStyle = "black";
- ctx.fillRect(0, 0, canvas.width, canvas.height);
- entities.map(function(elem) {
- elem.draw();
- });
- drawPlayer(translatePoints(player));
- meter.draw();
- ctx.fillStyle = "white";
- ctx.fillText(points+"", 5, 15);
- if(debug) {
- drawSpectrum();
- }
- }
- if(this.coverAlphaTween.promise.isPending()) {
- ctx.globalAlpha = this.coverAlphaTween.value;
- ctx.fillStyle = "black";
- ctx.fillRect(0, 0, canvas.width, canvas.height);
- ctx.globalAlpha = 1;
- }
- if(state.current == 'loading') {
- ctx.globalAlpha = this.textAlphaTween.value;
- ctx.fillStyle = 'white';
- ctx.textAlign = 'center';
- ctx.fillText("sound safari", canvas.width / 2, canvas.height / 2);
- ctx.textAlign = 'start';
- ctx.globalAlpha = 1;
- }
- };
- var onKeyUp = function(e) {
- if(e.keyCode == 80) {
- Game.pause();
- }
- else if(e.keyCode == 68) {
- debug = !debug;
- }
- };
- this.load = function() {
- addEventListener('keyup', onKeyUp);
- beats = [];
- var soundPromises = beatInfo.map(function(elem, index) {
- var beat = { info: elem, rounds: 0 };
- var promise = createSound('beat'+index, elem.url).then(function(sound) {
- beat.sound = sound;
- });
- beats.push(beat);
- return promise;
- });
- soundPromises.push(createSound('get', 'sound/get.mp3'));
- this.textAlphaTween = Tween
- .from(0)
- .to(1, 1.5)
- .wait(1)
- .waitFor(Q.all(soundPromises))
- .to(0, 1.5)
- .animate();
- this.coverAlphaTween = Tween
- .from(1)
- .waitFor(this.textAlphaTween.promise)
- .to(0, 1)
- .animate();
- this.textAlphaTween.promise.done(function() {
- state.ready();
- });
- };
- this.unload = function() {
- removeEventListener('keyup', onKeyUp);
- soundManager.destroySound('get');
- beats.forEach(function(beat) {beat.sound.destruct()});
- };
- this.pause = function() {
- removeEventListener('keyup', onKeyUp);
- currentBeats.map(function(beat) {beat.sound.pause()});
- };
- this.resume = function() {
- addEventListener('keyup', onKeyUp);
- currentBeats.map(function(beat) {beat.sound.resume()});
- };
- }
|