好久沒使用canvas了,於是透過寫小遊戲俄羅斯方塊再次熟悉下canvas,如果有一定的canvas基礎,要實現還是不難的。
原理詳解看遊戲最終介面,可知需要實現以下關鍵功能:
整個面板就是以左上角(0,0)為原點的座標系,右上角(12,0)左下角(0,20)右下角(12,20),每個點的座標位置都可以確定。是否已經填滿方塊,我們可以將每個方格看成一個陣列元素,0表示沒有,1表示已經填滿。 12 * 20 的面板使用兩層數組,即以20個長度為12的數組實現。
var maps = [[0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,1,0 ,1,0], ...];
畫出面板的程式碼,用最基礎的canvas的api就能實現
//格子for(var i=0;i<12;i++){ for(var j=0;j<20;j++){ ctx.fillRect(i*40,j*40,40,40); ctx. strokeRect(i*40,j*40,40,40); if(this.maps[j][i]==1){//方格已經有填滿內容ctx.save(); ctx.lineWidth=4; ctx.fillStyle='hsla(200,100%,50%,. 5)'; ctx.strokeStyle='hsla(200,100%,50%,.9)'; ctx.fillRect(i*40,j*40,40,40); ctx.strokeRect(i*40+2,j*40+2,38,38); ctx.restore(); } } } //邊框ctx.lineWidth=4; ctx.strokeStyle='hsla(0,100%,0%,.3)'; ctx.moveTo(0,0); ctx.lineTo(0,20*40); ctx.lineTo(12*40,20*40); ctx.lineTo(12*40,0); ctx.stroke(); ctx.restore();方塊的實現
遊戲中用到以下7 種圖形
結合上面介紹的座標系,陣列[x1, y1, x2, y2, x3, y3, x4, y4] 就是上面圖形中4個點座標的資料表現形式,7 種圖形的座標分別如下:
var Arr = [[4,0,4,1,5,1,6,1],[4,1,5,1,6,1,6,0],[4,0,5,0,5,1, 6,1],[4,1,5,0, 5,1,6,0],[5,0,4,1,5,1,6,1],[4,0,5,0,6,0,7,0],[5,0, 6,0,5,1,6,1]];
方塊的移動,遍歷整個數組,加上位移向量就行,非常簡單
class Shape { constructor(m){ this.m = Object.assign([],m); } move(x,y){ // 位移var m = this.m, l = m.length; y = y| |0; for (var i=0;i<l;i=i+2){ m[i]+=x; m[i+1]+=y; } return this; }
方塊的旋轉,俄羅斯方塊裡面方塊除了左右上下移動,還會旋轉,不是嗎?稍微思考下就知道,這不過就是矩陣變換而已,也就是每次圖形繞中心點旋轉90度。我這裡用陣列第三個點作為圖形變換的中心點,當然這樣處理不夠完善。
class Shape { transform(){//二維矩陣變換var m =this.m, l = m.length, c = Math.ceil(l/2), x = m[c], y = m[c+ 1], cos = Math.cos(Math.PI/180 * 90), sin = Math.sin(Math.PI/180 * 90); for (var i=0;i<l;i=i+2){ if(i == c) continue; var mx = m[i]- x, my = m[i+1] - y, nx = mx*cos - my*sin, ny = my*cos + mx*sin; m[i]=x+nx; m[i+1]=y+ny; } return this; }邊界條件
主要包括以下三個方面
遍歷數組(1)任一個點y座標為19時表示到達了底部;(2)獲取該座標的y+1位置在maps的信息,如果為1表示已經填充。這兩種情況下,運動方塊的週期結束,將該方塊的座標填入maps對應的陣列裡面即可。
如果座標的y+1已經有填充,同時當前座標小於1,即已經在介面的頂部了,那麼表示遊戲結束。
var isEnd = false,isOver=false,x,y;for(var i=0,sl=that.shape.m.length;i<sl;i=i+2){ x=that.shape.m[i ]; y=that.shape.m[i+1]; if(y >= 19){ // 到了底部isEnd = true;break; } if(that.maps[y+1][x]==1){ // y+1位置已經填入isEnd = true; if(y <= 1){isOver=true;} // 遊戲結束break; } }
方塊運動週期結束時偵測每一層是否滿格,以及滿格後的處理。某項數組全部元素都為1則表示已經滿格,那麼刪除該項數組,同時列表頭再壓入一項每個元素都為0的數組即可。
checkPoint(){ var that = this, maps = that.maps; for(var i=0,l=maps.length;i<l;i++){ if(Math.min.apply(null,maps[i]) == 1){// 表示該層已經滿格that.maps.splice(i,1); that.score+=10; //增加分數that.maps.unshift([0,0,0,0,0,0,0,0,0,0,0,0]); } } return this;}綁定事件
主要就是綁定keydown事件,要注意的是左移和右移事件包含了邊界判斷
bindEvent(){ var that = this; document.addEventListener('keydown',function(e){ switch(e.keyCode){ case 13: //enter cancelAnimationFrame(that.timer); that.init().update( ); break; case 80: //p that.pause = !that.pause; break; case 40: //down that.d = 0.5; break; case 37: //left var over = false, maps = that.maps, shape = that.shape, m = shape.m; for(var i=0,l=m .length;i<l;i=i+2){ if(m[i]<=0 || maps[m[i+1]][m[i]-1] == 1){ over = true;break; } } if(!over) shape.move(-1,0); break; case 39: //right var over = false, shape = that.shape, maps = that.maps , m = shape.m; for(var i=0,l=m.length;i<l;i=i+2){ if(m[i]>=11 || maps[m[i+1]][m[i]+1] == 1){ over = true;break; } } if(!over) shape.move(1,0); break; case 32: / /space that.shape.transform(); break; } },false);}總結
這裡面實現了俄羅斯方塊的最基本功能,還有關卡片等功能點並沒有實現,同時該demo仍有不完善的地方需要修正。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。