純JavaScript 實(shí)現(xiàn)flappy bird小游戲?qū)嵗a
前言:
《flappy bird》是一款由來(lái)自越南的獨(dú)立游戲開(kāi)發(fā)者Dong Nguyen所開(kāi)發(fā)的作品,游戲于2013年5月24日上線,并在2014年2月突然暴紅。2014年2月,《Flappy Bird》被開(kāi)發(fā)者本人從蘋(píng)果及谷歌應(yīng)用商店撤下。2014年8月份正式回歸APP STORE,正式加入Flappy迷們期待已久的多人對(duì)戰(zhàn)模式。游戲中玩家必須控制一只小鳥(niǎo),跨越由各種不同長(zhǎng)度水管所組成的障礙。
正文:
接下來(lái)就是一步一步來(lái)實(shí)現(xiàn)它
步驟1:頁(yè)面布局,這兒就不多說(shuō)了,頁(yè)面內(nèi)容如下:
步驟2:如何讓小鳥(niǎo)下落以及讓小鳥(niǎo)跳起來(lái)
鳥(niǎo)下降:
給小鳥(niǎo)一個(gè)speed,初始值為0,通過(guò)計(jì)時(shí)器讓speed每隔30ms加1,當(dāng)speed超出最大值speedMax,即speed>8時(shí),讓速度保持最大值。
//獲取鳥(niǎo)div var bird = document.getElementById("flybird"); //鳥(niǎo)下降 function down() { speed += 1; bird.id = 'flybird_down'; up_bgm.pause();//關(guān)閉小鳥(niǎo)上升音樂(lè); //當(dāng)鳥(niǎo)下落速度達(dá)到最大值speedMax時(shí),保持不變 if(speed >= speedMax) { speed = speedMax; } bird.style.top = bird.offsetTop + speed + 'px'; floorText(); //落地檢測(cè) }
鳥(niǎo)上升:
上升,即小鳥(niǎo)的top值減小的過(guò)程。讓speed減小即可。同時(shí),在鳥(niǎo)上升時(shí),關(guān)閉小鳥(niǎo)下降的計(jì)時(shí)器,以及上次起跳時(shí)的上升的計(jì)時(shí)器,并重新啟動(dòng)上升計(jì)時(shí)器。在這兒,有個(gè)isGameOver,為游戲開(kāi)關(guān),默認(rèn)為ture,即當(dāng)該值為false時(shí),游戲未開(kāi)始,小鳥(niǎo)無(wú)法起跳。
//小鳥(niǎo)上升 function up() { speed -= 0.8; bird.id = 'flybird_up'//該id下的樣式為小鳥(niǎo)下降的背景圖片,并增加動(dòng)畫(huà)不斷替換小鳥(niǎo)的背景圖像,讓小鳥(niǎo)翅膀動(dòng)起來(lái)~ up_bgm.play() if(speed <= 0) { speed = 0; clearInterval(upTimer); DownTimer = setInterval(down, 30); } bird.style.top = bird.offsetTop - speed + 'px'; } //鳥(niǎo)跳動(dòng)的方法; function birdJump() { speed = speedMax; if(isGameOver) { //每次向上跳時(shí),先將向上、向下計(jì)時(shí)器清楚,防止疊加 clearInterval(upTimer); clearInterval(DownTimer); //清除向下的定時(shí)器; upTimer = setInterval(up, 30); } } //判斷小鳥(niǎo)落地或者小鳥(niǎo)跳出上邊界,此時(shí)游戲結(jié)束 function floorText() { var t1 = bird.offsetTop; if(t1 > 396) { //游戲結(jié)束; gameover(); } if(t1 < 0) { gameover(); } }
步驟3:通過(guò)以上步驟,小鳥(niǎo)可以起跳啦。接下來(lái)就是管道的創(chuàng)建。玩過(guò)flappybird游戲的都知道,里面的管道的高度是隨機(jī)的,但上下兩個(gè)管道之間的距離是固定的。用Math.random()來(lái)產(chǎn)生隨機(jī)數(shù)。
//隨機(jī)函數(shù),用來(lái)隨機(jī)產(chǎn)生鋼管的高度 function rand(min, max) { return parseInt(Math.random() * (max - min) + min); } //創(chuàng)建管道。在點(diǎn)擊開(kāi)始按鈕后,通過(guò)計(jì)時(shí)器來(lái)創(chuàng)建 function create_pipe() { var conduit_group = document.querySelector(".conduit_group"); var conduitItem = document.createElement("div"); //給創(chuàng)建的管道一個(gè)樣式 conduitItem.className = 'conduitItem'; //將創(chuàng)建的管道放入外層div conduit_group.appendChild(conduitItem); var topHeight = rand(60, 223);//管道里面 上管道的高度值 var bottomHeight = 373 - 100 - topHeight;//管道里面 下管道的高度值 //創(chuàng)建上下管道 conduitItem.innerHTML = '<div class="top_conduit"><div style="height:' + topHeight + 'px"></div></div><div class="bottom_conduit"><div style="height:' + bottomHeight + 'px"></div></div>' //獲取最外層div的寬度,即為管道可以移動(dòng)的最大值 var maxWidth = canvas.offsetWidth; //讓管道剛開(kāi)始在canvas外面,一開(kāi)始看不到 conduitItem.style.left = maxWidth + 'px'; //加分開(kāi)關(guān),每通過(guò)一個(gè)管道分?jǐn)?shù)才能加1 conduitItem.AddToscore = true; //管道移動(dòng)方法,通過(guò)計(jì)時(shí)器不斷使其的left值遞減來(lái)實(shí)現(xiàn)管道移動(dòng)。 conduitItem.movetimer = setInterval(function() { maxWidth -= 3;//每30ms向左移動(dòng)3個(gè)像素 conduitItem.style.left = maxWidth + 'px' //在管道跑出去之后,清除使該管道移動(dòng)的計(jì)時(shí)器,并將其移除。 if(maxWidth <= -70) { clearInterval(conduitItem.movetimer); conduit_group.removeChild(conduitItem); } //當(dāng)管道的offsetLeft小于30時(shí),即小鳥(niǎo)成功通過(guò)管道之后,分?jǐn)?shù)加1 if(conduitItem.offsetLeft <= 30 && conduitItem.AddToscore == true) { score++; conduitItem.AddToscore = false; scoreFn(score); } }, 30) }
步驟4:通過(guò)以上步驟,移動(dòng)的管道創(chuàng)建好了,小鳥(niǎo)也可以跳了。接下來(lái)就是進(jìn)行碰撞檢測(cè),判斷小鳥(niǎo)是否碰到管道。
//鳥(niǎo)和管道碰撞檢測(cè),obj1為小鳥(niǎo),obj2為上下管道的父集 //直接獲取上下管道,offsetLeft為0,因此要獲取其父集; function crashTest(obj1, obj2) { //小鳥(niǎo)的相關(guān)量 var l1 = bird.offsetLeft; console.log(l1) var r1 = l1 + bird.offsetWidth; var t1 = bird.offsetTop; var b1 = t1 + bird.offsetHeight //管道的相關(guān)量 var l2 = obj2.offsetLeft; var r2 = l2 + obj2.offsetWidth; var t2 = obj1.offsetTop; var b2 = t2 + obj1.offsetHeight; //判斷條件 if(r1 > l2 && l1 < r2 && b1 > t2 && t1 < b2) { gameover(); } } function judge() { //獲取創(chuàng)造的在當(dāng)前頁(yè)面顯示的所有管道,為一個(gè)數(shù)組 var conduitItem = document.querySelector('.conduit_group').querySelectorAll('.conduitItem'); //遍歷顯示的管道,為crashTest方法傳遞參數(shù)來(lái)進(jìn)行判斷。 for(var i = 0; i < conduitItem.length; i++) { var top_conduit = conduitItem[i].querySelector('.top_conduit'); var bottom_conduit = conduitItem[i].querySelector('.bottom_conduit'); crashTest(top_conduit, conduitItem[i]); crashTest(bottom_conduit, conduitItem[i]); } }
步驟5:游戲結(jié)束方法。當(dāng)碰到管道,碰到上邊界,落地,游戲結(jié)束,顯示本局分?jǐn)?shù),并顯示歷史最高記錄。
//游戲結(jié)束 function gameover() { //游戲結(jié)束背景音樂(lè)打開(kāi) gameover_bgm.play(); isGameOver = false; //結(jié)束音樂(lè) bgm.pause(); clearTimer(); //小鳥(niǎo)換回原來(lái)的樣式 bird.id = 'flybird' bird.className = 'birddown' bird.style.top = '392px'; //存儲(chǔ)最高紀(jì)錄 var historyscore = localStorage.getItem('maxScore'); //當(dāng)歷史記錄不存在或者歷史記錄小于當(dāng)前記錄時(shí),創(chuàng)建masScore. if(historyscore == null || historyscore < score) { localStorage.setItem('maxScore', score); //刷新記錄 historyscore = score; } //歷史最高紀(jì)錄 historyScore.innerHTML = historyscore; //當(dāng)前分?jǐn)?shù) thisScore.innerHTML = score; //顯示游戲結(jié)束畫(huà)面 document.querySelector('.gameover').style.display = 'block'; }
步驟7:游戲開(kāi)始方法。
//游戲初始化 function init() { //為start_btn,即頁(yè)面剛開(kāi)始顯示的start創(chuàng)建點(diǎn)擊事件,即開(kāi)始按鈕 start_btn.onclick = function() { //點(diǎn)擊之后,開(kāi)始界面隱藏 gameStartDiv.style.display = 'none'; //小鳥(niǎo)顯示出來(lái) bird.style.display = 'block'; //使小鳥(niǎo)在中間顯示 bird.style.top = '200px'; bgm.play(); //通過(guò)點(diǎn)擊,來(lái)調(diào)用birdJump方法,來(lái)使小鳥(niǎo)上升 //開(kāi)始創(chuàng)造管道 conduitTimer = setInterval(create_pipe, 2000) document.addEventListener('click', birdJump, false) crashTestTimer = setInterval(judge, 1000 / 60); } } init();
步驟7:游戲重新開(kāi)始方法
//重新開(kāi)始 var game_restart = document.querySelector(".game_restart") game_restart.onclick = restart; var conduit_group = document.querySelector(".conduit_group") //回到剛開(kāi)始的界面 function restart() { bird.className = 'bird' clearTimer(); scoreDiv.innerHTML = null; isGameOver = true; speed = 0; score=0; speedMax = 8; document.querySelector('.gameover').style.display = 'none'; gameStartDiv.style.display = 'block'; bird.style.display = 'none'; conduit_group.innerHTML = ''; }
這兒用到的clearTimer方法為清除所有記時(shí)器,代碼如下:
function clearTimer() { var timer = setInterval(function() {}, 30); for(i = 0; i < timer; i++) { clearInterval(i); } }
這樣游戲大致已經(jīng)做好啦。
效果圖如下:
下面附上源碼下載地址,請(qǐng)?jiān)诠雀铻g覽器上進(jìn)行試驗(yàn)。
以上所述是小編給大家介紹的純JavaScript 實(shí)現(xiàn)flappy bird小游戲?qū)嵗a,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
JS 兩個(gè)字符串時(shí)間的天數(shù)差計(jì)算
本文為大家介紹下兩個(gè)字符串時(shí)間的天數(shù)差的計(jì)算公式,感興趣的朋友可以參考下2013-08-08微信小程序開(kāi)發(fā)注意指南和優(yōu)化實(shí)踐(小結(jié))
這篇文章主要介紹了微信小程序開(kāi)發(fā)注意指南和優(yōu)化實(shí)踐,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-06-06js 第二代身份證號(hào)碼的驗(yàn)證機(jī)制代碼
在盛大某網(wǎng)站注冊(cè)的時(shí)候,身份證必填,但我又不想填真實(shí)身份證號(hào)碼,于是隨便編了串自認(rèn)為合法的身份證號(hào)碼,但是卻馬上被提示號(hào)碼錯(cuò)誤2011-05-05漫談JS引擎的運(yùn)行機(jī)制 你應(yīng)該知道什么
javascript 從定義到執(zhí)行,你應(yīng)該知道的那些事,本文為大家一一列舉,希望對(duì)大家的學(xué)習(xí)有所幫助2016-06-06微信小程序通過(guò)uni-app進(jìn)行全局分享
這篇文章主要介紹了微信小程序通過(guò)uni-app進(jìn)行全局分享,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,需要的朋友可以收藏下2021-11-11利用js-cookie實(shí)現(xiàn)前端設(shè)置緩存數(shù)據(jù)定時(shí)失效
這篇文章主要介紹了利用js-cookie實(shí)現(xiàn)前端設(shè)置緩存數(shù)據(jù)定時(shí)失效,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06十分鐘打造AutoComplete自動(dòng)完成效果代碼
十分鐘打造山寨版谷歌AutoComplete,因?yàn)槭鞘昼姶蛟斐鰜?lái)的,所以只考慮表面效果,其他全部忽略,絕對(duì)的山寨。2009-12-12