HTML5中新增了Canvas元素,這個元素非常好玩,使用Canvas,我們可以使用代碼繪製出我們想要的圖形,用代碼繪圖,光是想想就興奮啊。
於是我在學習了Canvas的部分內容後,動手製作了一款小遊戲,這也是本人第一次獨立開發web項目,所以內行人看到細節肯定忍不住吐槽,希望大家批評指正,給出寶貴意見,我們共同進步。
一、遊戲介紹:
這個遊戲的界面非常簡單,左邊一塊用Canvas繪製的畫布,右邊有4個按鈕,左邊的畫布上有一個紅色的方塊和一個黑色的方塊,紅色的方塊是我們操作的對象,它是一個1×1×2的長方體,現在它正立在畫布上,將1×1的面朝向玩家,我們可以點擊右側的四個按鈕操作這個長方體,讓這個長方體在畫布上滾動,根據用戶的點擊,這個長方體會將不同的面朝向玩家,而玩家的目標就是通過操作這個長方體將它插入到畫布上黑色方塊所代表的洞中,另外,如果玩家的操作會導致長方體的任何部位懸空,那操作是無法執行的,會提示“無法前進”!目前這個遊戲的功能比較簡單,難度也不大,如果經過後期修改完善,增加更多無法前進的區域,這個遊戲是很有挑戰性的。
如果此時按左鍵,會彈出錯誤警告:
按下右鍵,也會導致長方體的部分懸空,所以也會彈出同樣的警告
最終我們要將長方體完整無誤地插入洞中,也就是大概做到這樣:
這時,只要按下向上,就能將長方體插入洞中,完成目標:
下面來談談具體實現。
二、設計思路及代碼實現
遊戲的實現主要是利用了Csnvas元素的繪圖方法,通過用戶點擊按鈕,產生指令,將指令傳遞給特定的函數,通過函數判斷長方體是否可以前進,將會產生怎樣的結果,然後通過Canvas重繪圖形。
1、首先,我們將遊戲界面繪製出來,
<canvas id="map" style="border:3px solid;float:left" width="500px" height="500px">你的瀏覽器不支持canvas</canvas> <!--按鈕--> <br /> <div id="control"> <div class="up" onClick="oTangle.moveUp()"><img src="arrow_up_128px_1137748_easyicon.net.png" /></div> <br /> <div class="left" onClick="oTangle.moveLeft()"><img src="arrow_left_128px_1137746_easyicon.net.png" /></div> <div class="down" onClick="oTangle.moveDown()"><img src="arrow_down_128px_1137745_easyicon.net.png" /></div> <div class="right" onClick="oTangle.moveRight()"><img src="arrow_right_128px_1137747_easyicon.net.png" /></div> </div>
以上代碼製作了界面的主體佈局,包括一張畫布,和包含4個作爲按鈕的div的控制區域。
然後爲以上元素添加樣式:
<style> div#control { float:left; width:500px; height:500px; position:absolute; left:500px; top:0; } div#control div { width:100px; height:100px; border-radius:25px; text-align:center; padding : 0px; box-shadow:2px 2px 1px #CCC; } div.up { position:absolute; left:200px; top:80px; } div.left { position:absolute; left:80px; top:200px; } div.right { position:absolute; left:320px; top:200px; } div.down { position:absolute; left:200px; top:320px; } img { opacity:0.2; filter:alpha(opacity=40); } img:hover { opacity:1.0; filter:alpha(opacity=100); } </style>
然後,使用以下代碼繪製地圖和黑洞:
var oLines = document.getElementById("map").getContext("2d"); for(var i =0;i<4; i++) { oLines.moveTo(100+100*i,0); oLines.lineTo(100+100*i,500); oLines.stroke(); oLines.moveTo(0,100+100*i); oLines.lineTo(500,100+100*i); oLines.stroke(); } oLines.save(); oLines.fillStyle="#000000"; oLines.fillRect(100,100,100,100); oLines.restore();
將以上代碼封裝到一個map()函數中,用於以後調用
然後,繪製我們操作的長方體,初始狀態下,長方體是立着的,代碼如下:
var tangle = document.getElementById("map").getContext("2d"); tangle.fillStyle = "#FF00FF"; tangle.fillRect(0,0,100,100);
至此,我們繪製出了遊戲所需要的遊戲界面,下面我們通過代碼實現遊戲的操作
2、定義對象
我們可以定義一個對象來代表這個長方體,這個對象應該包含以下屬性:
狀態:表示長方體當前是直立、橫躺還是豎躺的字符串變量。
座標:表示長方體在畫布上的位置(這裏用長方形左上角的點)的Number類型變量。
長、寬、高:用於重繪時進行計算的數字常量。
對象還應包含表示向各個方向滾動的4個方法,與四個按鈕的響應事件向對應
具體如下:
var oTangle = new Object; oTangle.sErect = "erect"; /*長方體的狀態*/ oTangle.x = 2; /*當前座標x*/ oTangle.y = 2; /*當前座標y*/ /*代表的顯示在地圖上的長方形的左上角的座標*/ oTangle.length = 100; oTangle.width = 100; oTangle.height = 200; oTangle.moveRight = function()/*長方體的4個方法*/ { move(oTangle.x,oTangle.y,oTangle.sErect,"right"); }; oTangle.moveLeft = function() { move(oTangle.x,oTangle.y,oTangle.sErect,"left"); }; oTangle.moveUp = function() { move(oTangle.x,oTangle.y,oTangle.sErect,"up"); }; oTangle.moveDown = function() { move(oTangle.x,oTangle.y,oTangle.sErect,"down"); };
然後,我們要抽象化這張畫布,方便長方體對象進行路徑判定,這裏可以使用一個數組表示:
var mapArray = new Array(); mapArray[0] = new Array(0,0,0,0,0,0,0,0,0); mapArray[1] = new Array(0,0,0,0,0,0,0,0,0); mapArray[2] = new Array(0,0,1,1,1,1,1,0,0); mapArray[3] = new Array(0,0,1,1,1,1,1,0,0); mapArray[4] = new Array(0,0,1,1,1,1,1,0,0); mapArray[5] = new Array(0,0,1,1,1,1,1,0,0); mapArray[6] = new Array(0,0,1,1,1,1,1,0,0); mapArray[7] = new Array(0,0,0,0,0,0,0,0,0); mapArray[8] = new Array(0,0,0,0,0,0,0,0,0);
在這裏,除了畫布本身的5×5個座標,我還把外圍2層也加入到數組中去了,這樣是爲了應對向外翻滾兩格的情況,當然,也可以使用instanceof運算符,判斷座標是否爲number類型,這樣就只需要建立一個5×5的二維數組就可以了,如果翻滾導致座標變成數組的length以外,將會返回Undefined,以此進行判定。
3、對象的方法——move()函數
我們創建一個函數,接受oTangle對象的屬性作爲參數,然後將參數傳遞到其他子程序中
function move(posiX,posiY,state,direction) { if(state=="erect") { if(ifSuspend(posiX,posiY,"erect",direction)) { draw(posiX,posiY,"erect",direction); map(); posiX=oTangle.x; posiY=oTangle.y; } else { alert("無法前進!"); } } 其他情況的判定...
在這個move()函數中,函數接受從對象的方法傳遞來的參數,調用一個判定前方是否爲路徑的函數,然後判斷函數的返回值是否爲true,即路徑合法,如果路徑合法,執行draw()函數和map()函數重繪圖形,並讓posiX、posiY與被修改的oTangle對象的座標屬性同步,如果路徑不合法,則彈窗報錯。根據長方體狀態的不同,共有3種判定情況,這裏只給出豎直情況的方法。
4、路徑判定函數——ifSuspend()函數
此函數接受從move()函數傳遞過來的oTangle對象屬性值,根據對象狀態的不同使用其座標(不是當前座標,而是玩家點擊操作後的座標值)和表示地圖的二維數組進行判定:
function ifSuspend(posiX2,posiY2,state2,direction2){ var arrX=posiX2,arrY=posiY2; if(state2=="erect") { if(direction2=="left") { if(mapArray[arrX-2][arrY]) { if((posiX2==4 && posiY2==3)||(posiX2==5 && posiY2==3)) { return 0; } else { return 1; } } else { return 0; } } if(direction2=="right") { if(mapArray[arrX+2][arrY]) { if((posiX2==1 && posiY2==3)||(posiX2==2 && posiY2==3)) { return 0; } else { return 1; } } else { return 0; } } if(direction2=="up") { if(mapArray[arrX][arrY-2]) { if((posiX2==3 && posiY2==4)||(posiX2==3 && posiY2==5)) { return 0; } else { return 1; } } else { return 0; } } if(direction2=="down") { if(mapArray[arrX][arrY+2]) { if((posiX2==3 && posiY2==2)||(posiX2==3 && posiY2==1)) { return 0; } else { return 1; } } else { return 0; } } } 其他判斷情況。。。
在函數的開始,新建了2個變量,將傳遞來的參數的值賦值給它們,因爲參與判定的是oTangle對象操作後的座標(此時還未判定是否允許操作),而不是當前的座標。
上面給出了豎直情況下的判定,豎直狀態下,向左和向上滾動會導致長方形左上角的座標發生2個單位的偏移,而向右和向下滾動則會產生1個單位的偏移。如圖:
紅色的點是當前座標,其他四個點的是將因玩家操作導致變化的結果,黑點相對紅點左移2個單位(arrX-2),黃點相對紅點右移2個單位(arrX+2),綠點相對紅點上移2個單位(arrY-2),紫點相對紅點下移1個單位(arrY-1).
在橫躺和豎躺的情況下,還要考慮插入黑洞的情況,如果操作將導致長方形的座標等於黑洞的座標,將退出判定,彈窗告訴玩家遊戲結束。
5、重繪圖形——draw()函數
draw()函數使用JavaScript代碼繪製圖形,這裏要調用前面繪製的tangle圖形對象。
首先調用save()方法保存當前狀態,主要是避免下次繪圖時改變原點的位置
然後調用clearRect()方法清除現在的圖形,接受4個參數,表示要清除的圖形的區域座標,(會一併清除地圖上的網格線,所以在上面的move()函數中再次調用了map(),事實上,在下面的代碼中,在clear()方法後面緊接着調用map(),就不會出現遊戲中網格線在長方體上的BUG了,不過,不要在意這些細節是不是?)
重繪的實現:首先,清除的區域是
((posiX4-2)*oTangle.width,(posiY4-2)*oTangle.width,oTangle.width,oTangle.length)
這個區域表示長方體當前的圖形,posiX4-2是因爲初始的座標是(2,2)(如果是用instanceof來判定,就不用這樣了,罪孽啊)
fillRect()方法重繪矩形,同樣接受4個參數,表示描繪矩形的區域,不同的狀態,不同的方法,作爲參數的公式也不相同,這裏只給出豎直狀態下左滾情況下的公式。
然後,回到前一個save()方法的狀態,避免繪圖導致原點偏移。
最後,修改長方體對象當前的座標,修改長方體對象當前的狀態,滾動完成。
function draw(posiX3,posiY3,state3,direction3) { var posiX4=oTangle.x,posiY4=oTangle.y; if(state3=="erect") { if(direction3=="left") { tangle.save(); tangle.clearRect((posiX4-2)*oTangle.width,(posiY4-2)*oTangle.width,oTangle.width,oTangle.length); tangle.fillRect((posiX4-4)*oTangle.width,(posiY4-2)*oTangle.width,oTangle.height,oTangle.width); tangle.restore(); oTangle.x-=2; oTangle.sErect="cross"; } 其他判斷情況。。。
使用以上代碼,就實現了遊戲流程,這個遊戲也就完成了
三、學習心得
這個遊戲功能(我實現的部分)很簡單,流程也不復雜,主要是使用了Canvas的繪製矩形的功能,很好理解,可以改進的地方也非常多,包括BUG也不少,不過畢竟這只是一個小小的作業,還有很多東西要學,所以後續的開發智能擱淺了。。。。