JAVASCRIPT實現FLY BIRD小遊戲

1.分析頁面結構,理清需求和功能

遊戲有三個界面,分別是開始界面,遊戲界面和遊戲結束界面。

1.1 開始界面


start.gif
  • 遊戲的大背景
  • 上下移動的遊戲標題和翅膀擺動的小鳥
  • start 按鈕,點擊進入遊戲界面
  • 一直移動的地面

    1.2 遊戲界面


play.gif
  • 顯示越過障礙數量的計分器
  • 移動的障礙物,分別是上管道和下管道
  • 點擊遊戲界面,小鳥向上飛起,然後在重力作用下下墜,
  • 當小鳥和管道碰撞後,結束界面彈出,同時小鳥落到地面

    1.3 結束界面

  • Game over 提示面板
  • OK 按鈕

2. 開發“開始界面”

考慮到草地的移動效果,我們在頁面中加入兩個草地

2.1 HTML

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Fly Bird</title>
        <link rel="stylesheet" type="text/css" href="css/index.css"/>
    </head>
    <body>
        <div id="wrapBg">  <!--遊戲背景-->
            <div id="headTitle"> <!--開始標題-->
                <img id="headBird" src="img/bird0.png" alt="小鳥" /> <!--標題中的小鳥-->
            </div>
            <button id="startBtn" ></button> <!--開始按鈕-->
            <div id="grassLand1"></div> <!--草地1-->
            <div id="grassLand2"></div> <!--草地2-->
        </div>
    </body>
</html>

2.2 CSS

#wrapBg{/*遊戲背景*/
    width: 343px;height: 480px; 
    margin: 0 auto;
    background-image:url(../img/bg.jpg);
    position: relative;
    top: 100px;
    overflow: hidden;
}
#headTitle{/*開始標題*/
    width: 236px;height: 77px;
    background-image: url(../img/head.jpg);
    position: absolute; left: 53px; top: 100px;
}
#headBird{/*開始標題中的小鳥*/
    float:right;
    margin-top: 25px;
}
#startBtn{/*開始按鈕*/
    width: 85px;height: 29px;
    padding: 0;margin: 0;
    background-image: url(../img/start.jpg);
    position: absolute;left: 129px;top: 250px;
}
#grassLand1{/*草地1*/
    height: 14px;width: 343px;
    background-image: url(../img/banner.jpg);
    position: absolute;top: 423px;
}
#grassLand2{/*草地2*/
    height: 14px;width: 343px;
    background-image: url(../img/banner.jpg);
    position: absolute;top: 423px;left: 343px;
}

將wrapBg中的overflow:hidden 註釋掉的頁面效果


開始界面.jpg

2.3 JS

小鳥煽動翅膀的效果需要用到逐幀動畫的原理

逐幀動畫是一種常見的動畫形式(Frame By Frame),其原理是在“連續的關鍵幀”中分解動畫動作,也就是在時間軸的每幀上逐幀繪製不同的內容,使其連續播放而成動畫。


bird1.png

bird0.png
2.3.1 開始標題的擺動
        var jsHeadTitle = document.getElementById("headTitle");// 獲取標題
        var jsHeadBird = document.getElementById("headBird"); // 獲取標題中小鳥

        var Y = 3;//標題的擺動幅度
        var index = 0;
        var imgArr = ["img/bird0.png","img/bird1.png"] 
        //將小鳥圖片路徑放入一個數組,利用逐幀動畫的原理做出小鳥翅膀擺動的樣子
        var headWaveTimer = setInterval(headWave,200); //設置標題上下襬動的定時器
        function headWave() {
            Y *= -1;
            jsHeadTitle.style.top = jsHeadTitle.offsetTop + Y + "px";
            jsHeadBird.src = imgArr[index++];
            if (index == 2) {
                index = 0;
            }
        }
2.3.2 移動的草地
        var jsGrassLand1 = document.getElementById("grassLand1"); //獲取草地1
        var jsGrassLand2 = document.getElementById("grassLand2"); //獲取草地2

        var landTimer = setInterval(landRun,30); //讓草地動起來的定時器
        function landRun() {
            if (jsGrassLand1.offsetLeft <= -343) {
                jsGrassLand1.style.left = "343px";
            }
            if (jsGrassLand2.offsetLeft <= -343) {
                jsGrassLand2.style.left = "343px";
            }
            jsGrassLand1.style.left = jsGrassLand1.offsetLeft - 3 + "px";
            jsGrassLand2.style.left = jsGrassLand2.offsetLeft - 3 + "px";
        }

2.3.3 Start按鍵

        var jsStartBtn = document.getElementById("startBtn");
        jsStartBtn.onclick = function() { //爲start按鍵添加點擊事件處理程序
            jsHeadTitle.style.display = "none"; //隱藏標題
            clearInterval(headWaveTimer); //關閉讓標題擺動的定時器
            jsStartBtn.style.display = "none"; //隱藏按鍵
            //待添加功能
            //點擊開始按鍵進入遊戲界面
        }

完成後的效果(註釋掉了wrapBg中的overflow:hidden )


start01.gif


接下來我們開發“遊戲界面”

3. “遊戲界面”的開發

遊戲界面中有三樣元素,分別是“小鳥”,“障礙”,和“計分器”,我們依次來創建相應的對象。

3.1 小鳥

首先,創建小鳥的對象, bird.js 文件。

var bird = {
    flyTimer:null,//小鳥飛翔定時器
    wingTimer:null,//小鳥翅膀擺動定時器

    div:document.createElement("div"),
    showBird:function(parentObj) {
        this.div.style.width = "40px";
        this.div.style.height = "28px";
        this.div.style.backgroundImage = "url(img/bird0.png)";
        this.div.style.backgroundRepeat = "no-repeat";
        this.div.style.position = "absolute";
        this.div.style.left = "50px";
        this.div.style.top = "200px";
        this.div.style.zIndex = "1";

        parentObj.appendChild(this.div);  //將小鳥DIV插入遊戲界面中
    },

    fallSpeed: 0, //小鳥下落速度
    flyBird: function(){ //控制小鳥飛翔下落的函數
        bird.flyTimer = setInterval(fly,40);
        function fly() {
            bird.div.style.top = bird.div.offsetTop + bird.fallSpeed++ + "px";
            if (bird.div.offsetTop < 0) {  
                bird.fallSpeed = 2; //這裏用於控制小鳥不要飛出界面
            }
            if (bird.div.offsetTop >= 395) {
                bird.fallSpeed = 0;
                clearInterval(bird.flyTimer); //一旦飛到地面,清除定時器
                clearInterval(bird.wingTimer); //清除翅膀擺動定時器
            }
            if (bird.fallSpeed > 12) {
                bird.fallSpeed = 12;  //鳥的最大下落速度控制在12
            }
        }
    },

    wingWave: function() { //控制小鳥煽動翅膀的函數
        var up = ["url(img/up_bird0.png)", "url(img/up_bird1.png)"];
        var down = ["url(img/down_bird0.png)", "url(img/down_bird1.png)"];
        var i = 0, j = 0;
        bird.wingTimer = setInterval(wing,120);//逐幀動畫,小鳥煽動翅膀
        function wing() {
            if (bird.fallSpeed > 0) {
                bird.div.style.backgroundImage = down[i++];
                if (i==2) {i = 0}
            }if (bird.fallSpeed < 0) {
                bird.div.style.backgroundImage = up[j++];
                if (j==2) {j = 0}
            }
        }
    },    
};

下面,實現點擊start按鈕時,加載小鳥。(在之前的代碼基礎上添加)

jsStartBtn.onclick = function() { //爲start按鍵添加點擊事件處理程序
    jsHeadTitle.style.display = "none"; //隱藏標題
    clearInterval(headWaveTimer); //關閉讓標題擺動的定時器
    jsStartBtn.style.display = "none"; //隱藏按鍵
    bird.showBird(jsWrapBg); //插入小鳥到界面中
    bird.flyBird(); //控制小鳥飛翔下落
    bird.wingWave(); //逐幀動畫,小鳥煽動翅膀
    jsWrapBg.onclick = function(){
        bird.fallSpeed = -8;
    };
    //待添加功能
    //點擊開始按鍵進入遊戲界面
}

添加小鳥後的效果


play01.gif

3.2 障礙(上管道和下管道)


block示意圖.png


障礙分爲上管道和下管道,如示意圖所示結構嵌套,這樣就可以通過隨機設置DownDiv2的高度和gapHeight的高度,來改變生成障礙的形態
block.js

function Block() {
    this.upDivWrap = null;
    this.downDivWrap = null;
    this.downHeight = baseObj.randomNum(0,150);//隨機生成0-150之間的數,用於控制下管道的高度
    this.gapHeight = baseObj.randomNum(150,160);// 管道中間間隙寬度,通過調節大小,可以的控制遊戲難度
    this.upHeight = 312 - this.downHeight - this.gapHeight;

    // 用來生成Div的方法
    this.createDiv = function(url, height, positionType, left, top) {
        var newDiv = document.createElement("div");
        newDiv.style.width = "62px";
        newDiv.style.height = height;
        newDiv.style.position = positionType;
        newDiv.style.left = left;
        newDiv.style.top = top;
        newDiv.style.backgroundImage = url;  //"url(/img/0.jpg)"
        return newDiv;
    };

    this.createBlock = function() {
        var upDiv1 = this.createDiv("url(img/up_mod.png)", this.upHeight + "px");
        var upDiv2 = this.createDiv("url(img/up_pipe.png)", "60px");
        this.upDivWrap = this.createDiv(null, null, "absolute", "450px");
        this.upDivWrap.appendChild(upDiv1);
        this.upDivWrap.appendChild(upDiv2);//生成上方管道

        var downDiv1 = this.createDiv("url(img/down_pipe.png)", "60px");
        var downDiv2 = this.createDiv("url(img/down_mod.png)", this.downHeight +"px");
        this.downDivWrap = this.createDiv(null, null, "absolute", "450px", 363 - this.downHeight + "px");
        this.downDivWrap.appendChild(downDiv1);
        this.downDivWrap.appendChild(downDiv2); //生成下方的管道

        jsWrapBg.appendChild(this.upDivWrap);
        jsWrapBg.appendChild(this.downDivWrap);
    };

    this.moveBlock = function() { //控制管道移動的方法
        this.upDivWrap.style.left = this.upDivWrap.offsetLeft - 3 + "px";
        this.downDivWrap.style.left = this.downDivWrap.offsetLeft - 3 + "px";
    };    
}

公共對象文件 baseObj.js ,用來提供隨機數,和兩個矩形div的碰撞檢測

var baseObj = {
    //隨機數
    randomNum: function(min, max) {
        return parseInt(Math.random() * (max - min + 1) + min);
    },

    //兩個矩形元素之間的碰撞檢測
    rectangleCrashExamine: function (obj1, obj2) {
            var obj1Left = obj1.offsetLeft;
            var obj1Width = obj1.offsetLeft + obj1.offsetWidth;
            var obj1Top = obj1.offsetTop;
            var obj1Height = obj1.offsetTop + obj1.offsetHeight;

            var obj2Left = obj2.offsetLeft;
            var obj2Width = obj2.offsetLeft + obj2.offsetWidth;
            var obj2Top = obj2.offsetTop;
            var obj2Height = obj2.offsetTop + obj2.offsetHeight;

            if (!(obj1Left > obj2Width || obj1Width < obj2Left || obj1Top > obj2Height || obj1Height < obj2Top)) {
                return true;
            }
            return false;
    },
};

下面我的想法是在start按鈕點擊的時候創建一個block,把這個block存儲到數組blocksArr 中,在 landTimer 定時器的方法 landRun 中檢查此數組的長度,如果數組不爲空數組,那麼就讓數組中所有的block移動。

檢查數組中最後一個block離開的距離,達到一定距離,就重新new 一個block,添加到數組。

檢查數組中第一個block,一旦達到一定位置,就在結構中移除downDivWrap 和 upDivWrap,同時在數組中刪除block。

        var blocksArr = [];
        var blockDistance = baseObj.randomNum(130,250);
        var landTimer = setInterval(landRun,30); //讓草地動起來的定時器
        function landRun() {
            if (jsGrassLand1.offsetLeft <= -343) {
                jsGrassLand1.style.left = "343px";
            }
            if (jsGrassLand2.offsetLeft <= -343) {
                jsGrassLand2.style.left = "343px";
            }
            jsGrassLand1.style.left = jsGrassLand1.offsetLeft - 3 + "px";
            jsGrassLand2.style.left = jsGrassLand2.offsetLeft - 3 + "px";

            if (blocksArr.length) {
                for (var i = 0; i < blocksArr.length; i++) {
                    blocksArr[i].moveBlock();
                    var x =baseObj.rectangleCrashExamine(blocksArr[i].downDivWrap, bird.div);
                    var y = baseObj.rectangleCrashExamine(blocksArr[i].upDivWrap, bird.div);
                    var z = bird.div.offsetTop >= 390;
                    if (x || y || z) {
                        window.clearInterval(landTimer);//清除landTimer定時器
                        bird.fallSpeed = 0; //小鳥下落
                        jsWrapBg.onclick = null; //消除點擊事件

                    }
                }
                if (blocksArr[blocksArr.length - 1].downDivWrap.offsetLeft < (450 - blockDistance)) {
                        blockDistance = baseObj.randomNum(130,250);
                        var newBlock = new Block();
                        newBlock.createBlock();
                        blocksArr.push(newBlock);
                }

                if (blocksArr[0].downDivWrap.offsetLeft < -50) {
                        jsWrapBg.removeChild(blocksArr[0].downDivWrap);
                        jsWrapBg.removeChild(blocksArr[0].upDivWrap);
                        blocksArr.shift(blocksArr[0]);
                }
            }
        }

當前的遊戲效果


play02.gif

3.3 計分器

遊戲中的計分器相對較好實現,我們就實現最大爲三位數的計分器吧。
html

        <div id="score">
            <div id="num1"></div>
            <div id="num2"></div>
            <div id="num3"></div>
        </div>

css樣式

#score{
    position:absolute;
    left: 130px;
    top:50px;
    z-index: 1;
}

#score div{
    height: 39px;
    width: 28px;
    float: left;
    background-image: url(../img/0.jpg);
    display: none;
}

js

        var jsScore = document.getElementById("score");
        var jsNum1 = document.getElementById("num1");
        var jsNum2 = document.getElementById("num2");
        var jsNum3 = document.getElementById("num3");
        var score = 0;

實現計數器功能,最重要的是如何判斷走過水管的數量,我們以水管的位置來判斷。bird的定位left爲50px,水管的寬度是62px,當水管越過小鳥的時候,水管距離它父級的定位offsetLeft 是 -12px。每當有一個水管到達此位置,score++;
在start按鈕的事件處理程序中加入

jsNum1.style.display = "block";// 在點擊開始之後,讓計數器顯示出來。
       if (blocksArr[0].downDivWrap.offsetLeft == -12) {
                score++;//積分面板
                if (score < 10) {
                jsNum1.style.backgroundImage = "url(img/" + score + ".jpg)";
            } else if (score < 100) {
                jsNum2.style.display = "block";
                jsNum1.style.backgroundImage = "url(img/" + parseInt(score/10) + ".jpg)";
                jsNum2.style.backgroundImage = "url(img/" + score%10 + ".jpg)";
            } else if (score < 1000) {
                jsNum3.style.display = "block";
                jsNum1.style.backgroundImage = "url(img/" + parseInt(score/100) + ".jpg)";
                jsNum2.style.backgroundImage = "url(img/" + parseInt(score/10)%10 + ".jpg)";
                jsNum3.style.backgroundImage = "url(img/" + score%10 + ".jpg)";
            }
                console.log(score);
        }

目前效果 ,計數器功能完成。


play03.gif

4.“結束界面”的開發

當小鳥和管道碰撞或者和地面碰撞時候,隱藏計分器,彈出結束面板。
結束界面主要有“結束面板”和“ok”按鈕,這裏需要爲“ok”按鈕添加點擊事件。

            <div id="gameOver">
                <img src="img/game_over.jpg" alt="game over" />
                <img src="img/message.jpg" alt="message" />
                <img id="ok" src="img/ok.jpg" alt="ok" />
            </div>
#gameOver{
    position: absolute;
    top: 100px;
    text-align: center;
    display: none;
    z-index: 1;
}

爲“OK”按鈕添加事件

        jsOkBtn.onclick = function() {
            window.location.href = "index.html"; //刷新頁面
        }

最終效果


play04.gif

有興趣的朋友,可以加羣下載代碼,然後加上音效

學習過程中遇到什麼問題或者想獲取學習資源的話,歡迎加入學習交流羣

343599877,我們一起學前端!
發佈了61 篇原創文章 · 獲贊 35 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章