Path: blob/main/projects/flappy-bird/js/main.js
1834 views
var debugmode = false;12var states = Object.freeze({3SplashScreen: 0,4GameScreen: 1,5ScoreScreen: 26});78var currentstate;910var gravity = 0.25;11var velocity = 0;12var position = 180;13var rotation = 0;14var jump = -4.6;15var flyArea = $("#flyarea").height();1617var score = 0;18var highscore = 0;1920var pipeheight = 90;21var pipewidth = 52;22var pipes = new Array();2324var replayclickable = false;2526//sounds27var volume = 30;28var soundJump = new buzz.sound("assets/sounds/sfx_wing.ogg");29var soundScore = new buzz.sound("assets/sounds/sfx_point.ogg");30var soundHit = new buzz.sound("assets/sounds/sfx_hit.ogg");31var soundDie = new buzz.sound("assets/sounds/sfx_die.ogg");32var soundSwoosh = new buzz.sound("assets/sounds/sfx_swooshing.ogg");33buzz.all().setVolume(volume);3435//loops36var loopGameloop;37var loopPipeloop;3839$(document).ready(function() {40if(window.location.search == "?debug")41debugmode = true;42if(window.location.search == "?easy")43pipeheight = 200;4445//get the highscore46var savedscore = getCookie("highscore");47if(savedscore != "")48highscore = parseInt(savedscore);4950//start with the splash screen51showSplash();52});5354function getCookie(cname)55{56var name = cname + "=";57var ca = document.cookie.split(';');58for(var i=0; i<ca.length; i++)59{60var c = ca[i].trim();61if (c.indexOf(name)==0) return c.substring(name.length,c.length);62}63return "";64}6566function setCookie(cname,cvalue,exdays)67{68var d = new Date();69d.setTime(d.getTime()+(exdays*24*60*60*1000));70var expires = "expires="+d.toGMTString();71document.cookie = cname + "=" + cvalue + "; " + expires;72}7374function showSplash()75{76currentstate = states.SplashScreen;7778//set the defaults (again)79velocity = 0;80position = 180;81rotation = 0;82score = 0;8384//update the player in preparation for the next game85$("#player").css({ y: 0, x: 0 });86updatePlayer($("#player"));8788soundSwoosh.stop();89soundSwoosh.play();9091//clear out all the pipes if there are any92$(".pipe").remove();93pipes = new Array();9495//make everything animated again96$(".animated").css('animation-play-state', 'running');97$(".animated").css('-webkit-animation-play-state', 'running');9899//fade in the splash100$("#splash").transition({ opacity: 1 }, 2000, 'ease');101}102103function startGame()104{105currentstate = states.GameScreen;106107//fade out the splash108$("#splash").stop();109$("#splash").transition({ opacity: 0 }, 500, 'ease');110111//update the big score112setBigScore();113114//debug mode?115if(debugmode)116{117//show the bounding boxes118$(".boundingbox").show();119}120121//start up our loops122var updaterate = 1000.0 / 60.0 ; //60 times a second123loopGameloop = setInterval(gameloop, updaterate);124loopPipeloop = setInterval(updatePipes, 1400);125126//jump from the start!127playerJump();128}129130function updatePlayer(player)131{132//rotation133rotation = Math.min((velocity / 10) * 90, 90);134135//apply rotation and position136$(player).css({ rotate: rotation, top: position });137}138139function gameloop() {140var player = $("#player");141142//update the player speed/position143velocity += gravity;144position += velocity;145146//update the player147updatePlayer(player);148149//create the bounding box150var box = document.getElementById('player').getBoundingClientRect();151var origwidth = 34.0;152var origheight = 24.0;153154var boxwidth = origwidth - (Math.sin(Math.abs(rotation) / 90) * 8);155var boxheight = (origheight + box.height) / 2;156var boxleft = ((box.width - boxwidth) / 2) + box.left;157var boxtop = ((box.height - boxheight) / 2) + box.top;158var boxright = boxleft + boxwidth;159var boxbottom = boxtop + boxheight;160161//if we're in debug mode, draw the bounding box162if(debugmode)163{164var boundingbox = $("#playerbox");165boundingbox.css('left', boxleft);166boundingbox.css('top', boxtop);167boundingbox.css('height', boxheight);168boundingbox.css('width', boxwidth);169}170171//did we hit the ground?172if(box.bottom >= $("#land").offset().top)173{174playerDead();175return;176}177178//have they tried to escape through the ceiling? :o179var ceiling = $("#ceiling");180if(boxtop <= (ceiling.offset().top + ceiling.height()))181position = 0;182183//we can't go any further without a pipe184if(pipes[0] == null)185return;186187//determine the bounding box of the next pipes inner area188var nextpipe = pipes[0];189var nextpipeupper = nextpipe.children(".pipe_upper");190191var pipetop = nextpipeupper.offset().top + nextpipeupper.height();192var pipeleft = nextpipeupper.offset().left - 2; // for some reason it starts at the inner pipes offset, not the outer pipes.193var piperight = pipeleft + pipewidth;194var pipebottom = pipetop + pipeheight;195196if(debugmode)197{198var boundingbox = $("#pipebox");199boundingbox.css('left', pipeleft);200boundingbox.css('top', pipetop);201boundingbox.css('height', pipeheight);202boundingbox.css('width', pipewidth);203}204205//have we gotten inside the pipe yet?206if(boxright > pipeleft)207{208//we're within the pipe, have we passed between upper and lower pipes?209if(boxtop > pipetop && boxbottom < pipebottom)210{211//yeah! we're within bounds212213}214else215{216//no! we touched the pipe217playerDead();218return;219}220}221222223//have we passed the imminent danger?224if(boxleft > piperight)225{226//yes, remove it227pipes.splice(0, 1);228229//and score a point230playerScore();231}232}233234//Handle space bar235$(document).keydown(function(e){236//space bar!237if(e.keyCode == 32)238{239//in ScoreScreen, hitting space should click the "replay" button. else it's just a regular spacebar hit240if(currentstate == states.ScoreScreen)241$("#replay").click();242else243screenClick();244}245});246247//Handle mouse down OR touch start248if("ontouchstart" in window)249$(document).on("touchstart", screenClick);250else251$(document).on("mousedown", screenClick);252253function screenClick()254{255if(currentstate == states.GameScreen)256{257playerJump();258}259else if(currentstate == states.SplashScreen)260{261startGame();262}263}264265function playerJump()266{267velocity = jump;268//play jump sound269soundJump.stop();270soundJump.play();271}272273function setBigScore(erase)274{275var elemscore = $("#bigscore");276elemscore.empty();277278if(erase)279return;280281var digits = score.toString().split('');282for(var i = 0; i < digits.length; i++)283elemscore.append("<img src='assets/font_big_" + digits[i] + ".png' alt='" + digits[i] + "'>");284}285286function setSmallScore()287{288var elemscore = $("#currentscore");289elemscore.empty();290291var digits = score.toString().split('');292for(var i = 0; i < digits.length; i++)293elemscore.append("<img src='assets/font_small_" + digits[i] + ".png' alt='" + digits[i] + "'>");294}295296function setHighScore()297{298var elemscore = $("#highscore");299elemscore.empty();300301var digits = highscore.toString().split('');302for(var i = 0; i < digits.length; i++)303elemscore.append("<img src='assets/font_small_" + digits[i] + ".png' alt='" + digits[i] + "'>");304}305306function setMedal()307{308var elemmedal = $("#medal");309elemmedal.empty();310311if(score < 10)312//signal that no medal has been won313return false;314315if(score >= 10)316medal = "bronze";317if(score >= 20)318medal = "silver";319if(score >= 30)320medal = "gold";321if(score >= 40)322medal = "platinum";323324elemmedal.append('<img src="assets/medal_' + medal +'.png" alt="' + medal +'">');325326//signal that a medal has been won327return true;328}329330function playerDead()331{332//stop animating everything!333$(".animated").css('animation-play-state', 'paused');334$(".animated").css('-webkit-animation-play-state', 'paused');335336//drop the bird to the floor337var playerbottom = $("#player").position().top + $("#player").width(); //we use width because he'll be rotated 90 deg338var floor = flyArea;339var movey = Math.max(0, floor - playerbottom);340$("#player").transition({ y: movey + 'px', rotate: 90}, 1000, 'easeInOutCubic');341342//it's time to change states. as of now we're considered ScoreScreen to disable left click/flying343currentstate = states.ScoreScreen;344345//destroy our gameloops346clearInterval(loopGameloop);347clearInterval(loopPipeloop);348loopGameloop = null;349loopPipeloop = null;350351//mobile browsers don't support buzz bindOnce event352if(isIncompatible.any())353{354//skip right to showing score355showScore();356}357else358{359//play the hit sound (then the dead sound) and then show score360soundHit.play().bindOnce("ended", function() {361soundDie.play().bindOnce("ended", function() {362showScore();363});364});365}366}367368function showScore()369{370//unhide us371$("#scoreboard").css("display", "block");372373//remove the big score374setBigScore(true);375376//have they beaten their high score?377if(score > highscore)378{379//yeah!380highscore = score;381//save it!382setCookie("highscore", highscore, 999);383}384385//update the scoreboard386setSmallScore();387setHighScore();388var wonmedal = setMedal();389390//SWOOSH!391soundSwoosh.stop();392soundSwoosh.play();393394//show the scoreboard395$("#scoreboard").css({ y: '40px', opacity: 0 }); //move it down so we can slide it up396$("#replay").css({ y: '40px', opacity: 0 });397$("#scoreboard").transition({ y: '0px', opacity: 1}, 600, 'ease', function() {398//When the animation is done, animate in the replay button and SWOOSH!399soundSwoosh.stop();400soundSwoosh.play();401$("#replay").transition({ y: '0px', opacity: 1}, 600, 'ease');402403//also animate in the MEDAL! WOO!404if(wonmedal)405{406$("#medal").css({ scale: 2, opacity: 0 });407$("#medal").transition({ opacity: 1, scale: 1 }, 1200, 'ease');408}409});410411//make the replay button clickable412replayclickable = true;413}414415$("#replay").click(function() {416//make sure we can only click once417if(!replayclickable)418return;419else420replayclickable = false;421//SWOOSH!422soundSwoosh.stop();423soundSwoosh.play();424425//fade out the scoreboard426$("#scoreboard").transition({ y: '-40px', opacity: 0}, 1000, 'ease', function() {427//when that's done, display us back to nothing428$("#scoreboard").css("display", "none");429430//start the game over!431showSplash();432});433});434435function playerScore()436{437score += 1;438//play score sound439soundScore.stop();440soundScore.play();441setBigScore();442}443444function updatePipes()445{446//Do any pipes need removal?447$(".pipe").filter(function() { return $(this).position().left <= -100; }).remove()448449//add a new pipe (top height + bottom height + pipeheight == flyArea) and put it in our tracker450var padding = 80;451var constraint = flyArea - pipeheight - (padding * 2); //double padding (for top and bottom)452var topheight = Math.floor((Math.random()*constraint) + padding); //add lower padding453var bottomheight = (flyArea - pipeheight) - topheight;454var newpipe = $('<div class="pipe animated"><div class="pipe_upper" style="height: ' + topheight + 'px;"></div><div class="pipe_lower" style="height: ' + bottomheight + 'px;"></div></div>');455$("#flyarea").append(newpipe);456pipes.push(newpipe);457}458459var isIncompatible = {460Android: function() {461return navigator.userAgent.match(/Android/i);462},463BlackBerry: function() {464return navigator.userAgent.match(/BlackBerry/i);465},466iOS: function() {467return navigator.userAgent.match(/iPhone|iPad|iPod/i);468},469Opera: function() {470return navigator.userAgent.match(/Opera Mini/i);471},472Safari: function() {473return (navigator.userAgent.match(/OS X.*Safari/) && ! navigator.userAgent.match(/Chrome/));474},475Windows: function() {476return navigator.userAgent.match(/IEMobile/i);477},478any: function() {479return (isIncompatible.Android() || isIncompatible.BlackBerry() || isIncompatible.iOS() || isIncompatible.Opera() || isIncompatible.Safari() || isIncompatible.Windows());480}481};482483