一直想本身作點小東西,直到最近看了本《HTML5遊戲開發》,才瞭解遊戲開發中的一點點入門知識。javascript
本篇就針對學習的幾個樣例,本身動手實踐,作了個FlappyBird,源碼共享在度盤 ;也能夠參考github,裏面有更多的遊戲樣例。html
Canvas是Html5中用於繪圖的元素,它能夠繪製各類圖形,好比長方形,多邊形,圓形等等。若是想要了解Canvas的使用能夠參考:java
http://www.w3school.com.cn/tags/html_ref_canvas.aspgit
//若是想要使用canvas,首先須要得到上下文對象: ctx = document.getElementById('canvas').getContext('2d'); //而後使用這個ctx繪製圖形
在cavas每一個繪製都是獨立的操做。好比下圖的兩個繪製圖形,第二個會以覆蓋的形式繪製,所以繪製圖形的順序就顯得十分重要了。github
本篇的遊戲開發中,主要使用的是依據圖片繪製的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
其實這個遊戲很簡單,一張圖就能夠看懂其中的奧妙: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>
在學習遊戲開發的時候,我忽然懷念起大學的物理。當時很納悶,學計算機學什麼物理,後來再接觸遊戲開發才知道,沒有必定的物理知識,根本沒法模擬遊戲中的各個場景。
而經過這個簡單的小遊戲,也撿起來了不少舊知識。
【1】:Canvas參考手冊
【2】:《HTML5遊戲開發》
【3】:EdisonChou的FlappyBird