[Canvas前端遊戲開發]——FlappyBird詳解

一直想本身作點小東西,直到最近看了本《HTML5遊戲開發》,才瞭解遊戲開發中的一點點入門知識。javascript

本篇就針對學習的幾個樣例,本身動手實踐,作了個FlappyBird,源碼共享在度盤 ;也能夠參考github,裏面有更多的遊戲樣例。html

遊戲截圖

HTML5之Canvas

Canvas是Html5中用於繪圖的元素,它能夠繪製各類圖形,好比長方形,多邊形,圓形等等。若是想要了解Canvas的使用能夠參考:java

http://www.w3school.com.cn/tags/html_ref_canvas.aspgit

 

//若是想要使用canvas,首先須要得到上下文對象:
ctx = document.getElementById('canvas').getContext('2d');
//而後使用這個ctx繪製圖形

在cavas每一個繪製都是獨立的操做。好比下圖的兩個繪製圖形,第二個會以覆蓋的形式繪製,所以繪製圖形的順序就顯得十分重要了。github

canvas之drawImage()

本篇的遊戲開發中,主要使用的是依據圖片繪製的api:drawImage(),它有兩個基本的使用方法:canvas

ctx.drawImage(image,this.bx,this.by,this.bwidth,this.bheight);
ctx.drawImage(image,x,y,width,height,this.px,this.py,this.pwidth,this.pheight);

第一個api中,指定Image對象,而後給出繪製圖片的x,y座標以及寬度和高度便可。api

第二個api中,第一組x,y,width,height則指定了裁剪圖片的座標尺寸,這在使用多元素的矢量圖時很經常使用。好比:數組

上面的圖片中爲了減小圖片資源的請求數量,把不少的元素放在了一個圖片中,此時就須要經過裁剪的方式,獲取指定的圖片元素。app

FlappyBird原理解析

其實這個遊戲很簡單,一張圖就能夠看懂其中的奧妙:dom

其中背景和地面是不動的。

小鳥只有上和下兩個動做,能夠經過控制小鳥的y座標實現。

上下的管子只會向左移動,爲了簡單實現,遊戲中一個畫面僅僅會出現一對管子,這樣當管子移出左邊的背景框,就自動把管子放在最右邊!

if(up_pipe.px+up_pipe.pwidth>0){
                up_pipe.px -= velocity;
                down_pipe.px -= velocity;
            }else{
                up_pipe.px = 400;
                down_pipe.px = 400;
                up_pipe.pheight = 100+Math.random()*200;
                down_pipe.py = up_pipe.pheight+pipe_height;
                down_pipe.pheight = 600-down_pipe.py;
                isScore = true;
            }

很簡單吧!

因爲該遊戲一共就這幾個元素,所以把他們都放入一個Objects數組中,經過setInteral()方法,在必定間隔時間內,執行一次重繪

重繪的時候會先清除畫面中的全部元素,而後按照新的元素的座標一次繪製圖形,這樣就會出現移動的效果。

模擬小鳥重力

因爲這個遊戲不涉及小鳥橫向的運動,所以只要模擬出小鳥下落的動做以及上升的動做就能夠了。

上升:這個很簡單,只要把小鳥的y座標減去必定的值就能夠了

下落:其實重力不須要使用gt^2來模擬,能夠簡單的指定兩個變量,v1和gravity,這兩個變量與setInterval()中的時間共同做用,就能模擬重力。

ver2 = ver1+gravity;
bird.by += (ver2+ver1)*0.5;

碰撞檢測

遊戲中小鳥碰到管子或者地面都會算遊戲結束:

其中條件1上管道的檢測爲:

((bird.bx+bird.bwidth>up_pipe.px)&&(bird.by>up_pipe.py)&&(bird.bx+bird.bwidth<up_pipe.px+up_pipe.pwidth)&&(bird.by<up_pipe.py+up_pipe.pheight))||
((bird.bx+bird.bwidth>up_pipe.px)&&(bird.by>up_pipe.py)&&(bird.bx+bird.bwidth<up_pipe.px+up_pipe.pwidth)&&(bird.by<up_pipe.py+up_pipe.pheight))

條件2下管道的檢測爲:

((bird.bx>down_pipe.px)&&(bird.by>down_pipe.py)&&(bird.bx<down_pipe.px+down_pipe.pwidth)&&(bird.by<down_pipe.py+down_pipe.pheight))||
((bird.bx>down_pipe.px)&&(bird.by+bird.bheight>down_pipe.py)&&(bird.bx<down_pipe.px+down_pipe.pwidth)&&(bird.by+bird.bheight<down_pipe.py+down_pipe.pheight))

條件3地面的檢測最簡單,爲:

bird.by+bird.bheight>ground.bgy

若是知足這三個條件,就算遊戲結束,會清除循環以及提示遊戲結束信息。

分數計算

分數的計算與碰撞檢測相似,設置一個開關,當管子從新出現時,設置爲true。當分值加1時,設置爲false。

小鳥的最左邊的x座標若是超出了管子的x+width,就認爲成功經過。

if(isScore && bird.bx>up_pipe.px+up_pipe.pwidth){
                score += 1;
                isScore = false;
                if(score>0 && score%10 === 0){
                    velocity++;
                }
            }

經過後,分值加1,速度+1。

所有源碼

<!DOCTYPE html>
<html>
<head>
    <title>Flappy Bird</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <script type="text/javascript">
        // Edit by xingoo
        // Fork on my github:https://github.com/xinghalo/CodeJS/tree/master/HTML5
        var ctx;
        var cwidth = 400;
        var cheight = 600;
        var objects = [];
        var birdIndex = 0;
        var ver1 = 10;
        var ver2;
        var gravity = 2;
        var pipe_height = 200;
        var velocity = 10;
        var tid;
        var score = 0;
        var isScore = false;
        var birds = ["./images/0.gif","./images/1.gif","./images/2.gif"];
        var back = new Background(0,0,400,600,"./images/bg.png");
        var up_pipe = new UpPipe(0,0,100,200,"./images/pipe.png");
        var down_pipe = new DownPipe(0,400,100,200,"./images/pipe.png");
        var ground = new Background(0,550,400,200,"./images/ground.png");
        var bird = new Bird(80,300,40,40,birds);
        objects.push(back);
        objects.push(up_pipe);
        objects.push(down_pipe);
        objects.push(ground);
        objects.push(bird);
        function UpPipe(x,y,width,height,img_src){
            this.px = x;
            this.py = y;
            this.pwidth = width;
            this.pheight = height;
            this.img_src = img_src;
            this.draw = drawUpPipe;
        }
        function DownPipe(x,y,width,height,img_src){
            this.px = x;
            this.py = y;
            this.pwidth = width;
            this.pheight = height;
            this.img_src = img_src;
            this.draw = drawDownPipe;
        }
        function drawUpPipe(){
            var image = new Image();
            image.src = this.img_src;
            ctx.drawImage(image,150,500,150,800,this.px,this.py,this.pwidth,this.pheight);
        }
        function drawDownPipe(){
            var image = new Image();
            image.src = this.img_src;
            ctx.drawImage(image,0,500,150,500,this.px,this.py,this.pwidth,this.pheight);
        }
        function Background(x,y,width,height,img_src){
            this.bgx = x;
            this.bgy = y;
            this.bgwidth = width;
            this.bgheight = height;
            var image = new Image();
            image.src = img_src;
            this.img = image;
            this.draw = drawbg;
        }
        function drawbg(){
            ctx.drawImage(this.img,this.bgx,this.bgy,this.bgwidth,this.bgheight);
        }
        function Bird(x,y,width,height,img_srcs){
            this.bx = x;
            this.by = y;
            this.bwidth = width;
            this.bheight = height;
            this.imgs = img_srcs;
            this.draw = drawbird;
        }
        function drawbird(){
            birdIndex++;
            var image = new Image();
            image.src = this.imgs[birdIndex%3];
            ctx.drawImage(image,this.bx,this.by,this.bwidth,this.bheight);
        }
        function calculator(){
            if(bird.by+bird.bheight>ground.bgy ||
                ((bird.bx+bird.bwidth>up_pipe.px)&&(bird.by>up_pipe.py)&&(bird.bx+bird.bwidth<up_pipe.px+up_pipe.pwidth)&&(    bird.by<up_pipe.py+up_pipe.pheight))||
                ((bird.bx+bird.bwidth>up_pipe.px)&&(bird.by>up_pipe.py)&&(bird.bx+bird.bwidth<up_pipe.px+up_pipe.pwidth)&&(    bird.by<up_pipe.py+up_pipe.pheight))||
                ((bird.bx>down_pipe.px)&&(bird.by>down_pipe.py)&&(bird.bx<down_pipe.px+down_pipe.pwidth)&&(bird.by<down_pipe.py+down_pipe.pheight))||
                ((bird.bx>down_pipe.px)&&(bird.by+bird.bheight>down_pipe.py)&&(bird.bx<down_pipe.px+down_pipe.pwidth)&&(bird.by+bird.bheight<down_pipe.py+down_pipe.pheight))){
                clearInterval(tid);
                ctx.fillStyle = "rgb(255,255,255)";
                ctx.font = "30px Accent";
                ctx.fillText("You got "+score+"!",110,100)
                return;
            }
            ver2 = ver1+gravity;
            bird.by += (ver2+ver1)*0.5;
            if(up_pipe.px+up_pipe.pwidth>0){
                up_pipe.px -= velocity;
                down_pipe.px -= velocity;
            }else{
                up_pipe.px = 400;
                down_pipe.px = 400;
                up_pipe.pheight = 100+Math.random()*200;
                down_pipe.py = up_pipe.pheight+pipe_height;
                down_pipe.pheight = 600-down_pipe.py;
                isScore = true;
            }
            if(isScore && bird.bx>up_pipe.px+up_pipe.pwidth){
                score += 1;
                isScore = false;
                if(score>0 && score%10 === 0){
                    velocity++;
                }
            }
            ctx.fillStyle = "rgb(255,255,255)";
            ctx.font = "30px Accent";
            if(score>0){
                score%10!==0?ctx.fillText(score,180,100):ctx.fillText("Great!"+score,120,100);
            }
        }
        function drawall(){
            ctx.clearRect(0,0,cwidth,cheight);
            var i;
            for(i=0;i<objects.length;i++){
                objects[i].draw();
            }
            calculator();
        }
        function keyup(e){
            var e = e||event;
               var currKey = e.keyCode||e.which||e.charCode;
               switch (currKey){
                case 32:
                    bird.by -= 80;
                    break;
            }
        }    
        function init(){
            ctx = document.getElementById('canvas').getContext('2d');
            document.onkeyup = keyup;
            drawall();
            tid = setInterval(drawall,80);
        }
    </script>
</head>
<body onLoad="init();">
<canvas id="canvas" width="400" height="600" style="margin-left:200px;">
    Your browser is not support canvas! 
</canvas>
</body>
</html>
View Code

總結

在學習遊戲開發的時候,我忽然懷念起大學的物理。當時很納悶,學計算機學什麼物理,後來再接觸遊戲開發才知道,沒有必定的物理知識,根本沒法模擬遊戲中的各個場景。

而經過這個簡單的小遊戲,也撿起來了不少舊知識。

參考

【1】:Canvas參考手冊

【2】:《HTML5遊戲開發

【3】:EdisonChou的FlappyBird

相關文章
相關標籤/搜索