接着上一篇(
http://www.cnblogs.com/zhouhuan/p/H5_tankgame.html),這一篇研究一下怎么響應玩家的操作讓坦克進行相應的移動。
1. 了解keydown事件
keydown這一鍵盤事件的觸發條件為按下鍵盤上的任意鍵,如果按住不放,則會重發觸發。
示例:
window.onkeydown = function(){ alert("Merry Christmas!"); };
此時載入頁面之后,無論按下哪個鍵,都會彈出“Merry Christmas!”的彈窗。
2. 了解鍵碼和字符編碼
① 鍵碼
在發生keydown和keyup事件時,event對象的keyCode(鍵碼)屬性會包含一個代碼,與鍵盤上一個特定的鍵對應。對於數字字母字符集,keyCode屬性的值與ASCII碼中對應小寫字母或數字的編碼相同。字母中的大小寫不影響。
window.onkeydown = function(eve){ alert(eve.keyCode); };
此時按鍵盤上的任意鍵,便可得到所按鍵對應的keyCode
② 字符編碼
發生keypress事件時,event對象的charCode屬性會包含一個值,這個值就是按下的那個鍵所代表字符的ASCII編碼,並且,同一個字母的大小寫對應的字符編碼也是不一樣的。
要注意的是,keypress事件只有在按下字符鍵時才會觸發,並不是所有的按鍵,像Ctrl, Alt之類的就不會觸發該事件。
3. 熱身環節
① 獲取玩家的指令
我們先看看怎么獲取到玩家的操控指令,這里我們寫一段代碼:
window.onkeydown = function(eve){ alert("所按鍵對應的鍵碼是: " + eve.keyCode); };
大家運行一下就可以知道鍵盤上每一個按鍵所對應的鍵碼是多少了,然后取自己需要的按鍵繼續編寫程序。這里需要的是方向鍵的上下左右,當然這個在網上可以查到,也非常方便。
我們運行了之后會發現,上下左右對應的鍵碼分別是38, 40, 37, 39。考慮到有些玩家習慣於使用W A S D來操作,那我們把這幾個鍵也做進去,這幾個鍵對應的鍵碼分別是87, 65, 83, 68。
OK,知道了上面這些東西之后我們就可以寫出下面這段代碼了:
window.onkeydown = function(eve){ if(eve.keyCode == 38 || eve.keyCode == 87){ alert("上"); }else if(eve.keyCode == 40 || eve.keyCode == 83){ alert("下"); }else if(eve.keyCode == 37 || eve.keyCode == 65){ alert("左"); }else if(eve.keyCode == 39 || eve.keyCode == 68){ alert("右"); } };
此時,根據玩家的操作便能彈出相應方向的文字。
鑒於上面if語句的條件分支數量略多,我們最好用switch語句改寫一下上面的代碼,這樣可以提高性能,如下:
window.onkeydown = function(eve){ switch(eve.keyCode){ case 38: case 87: alert("上"); break; case 40: case 83: alert("下"); break; case 37: case 65: alert("左"); break; case 39: case 68: alert("右"); } };
② 封裝畫坦克的函數
前面我們寫的畫坦克的代碼其實是面向過程的,我們將它拿過來改巴改巴做以封裝:
function drawTank(x,y){ var myCanvas = document.getElementById('floor'); var cxt = myCanvas.getContext('2d'); cxt.fillStyle = "#542174"; cxt.fillRect(x,y,20,65); cxt.fillRect(x+70,y,20,65); cxt.fillRect(x+23,y+10,44,50); cxt.fillStyle = "#FCB827"; cxt.beginPath(); cxt.arc(x+45,y+35,16,0,2*Math.PI,false); cxt.closePath(); cxt.fill(); cxt.strokeStyle = "#FCB827"; cxt.lineWidth = "8.0"; cxt.moveTo(x+45,y+35); cxt.lineTo(x+45,y-25); cxt.stroke(); }
這個函數調用的時候傳兩個參數(x, y),分別代表坦克左上角的X坐標,Y坐標。
封裝好之后,在任何地方只要一調用,便可以造出一個坦克了:
drawTank(150,200); //以(150,200)的點為坦克的左上角(左邊履帶的左上角)造一個坦克
③ 了解clearRect()方法
有一個前面遺漏掉的知識點clearRect()方法,這個方法是做游戲的關鍵,用來清空指定矩形內的所有像素,傳四個參數(x, y, width, height),前兩個參數表示要清除的矩形的左上角坐標,后兩個參數表示要清除的矩形的寬高。
比如我們先畫一個矩形:
var myCanvas = document.getElementById('floor'); var cxt = myCanvas.getContext('2d'); cxt.fillStyle = "orange"; cxt.fillRect(50,50,300,80);
得到:
我們再加上下面這句代碼運行一下:
cxt.clearRect(0,0,800,500);
此時會發現整個畫布又空空如也了,因為我們把整個畫布的像素都清除掉了。
4. 熱身完畢,正式開始
前面熱身熱了這么久,相信大家已經可以寫出一個根據玩家的操作移動的坦克了。
我們盡量以面向對象的思想來寫每一個過程,代碼如下:
//封裝一個獲取繪圖環境的函數 function getCxt(){ var myCanvas = document.getElementById('floor'), myContext = myCanvas.getContext('2d'); return myContext; } //封裝一個畫坦克的函數,傳兩個參數x,y,分別代表左上角的橫縱坐標 function drawTank(x,y){ var cxt = getCxt(); cxt.fillStyle = "#542174"; cxt.fillRect(x,y,20,65); cxt.fillRect(x+70,y,20,65); cxt.fillRect(x+23,y+10,44,50); cxt.fillStyle = "#FCB827"; cxt.beginPath(); cxt.arc(x+45,y+35,16,0,2*Math.PI,false); cxt.closePath(); cxt.fill(); cxt.strokeStyle = "#FCB827"; cxt.lineWidth = "8.0"; cxt.moveTo(x+45,y+35); cxt.lineTo(x+45,y-25); cxt.stroke(); } //初始化一個對象myTank,用來存儲一些屬性和方法 var myTank = {}; myTank.x = 350; myTank.y = 400; myTank.step = 3; //設置步長,步長越大那么坦克運動的速度越快 //先畫一個坦克出來 drawTank(myTank.x,myTank.y); //封裝一個更新戰場的函數 function updateFloor(){ var cxt = getCxt(); cxt.clearRect(0,0,800,500); //更新之前先清除畫布 drawTank(myTank.x,myTank.y); //清除完之后重新造坦克,坦克要移動就必須實時地根據坐標重新來造 } //設置一個間歇調用的函數,每隔100ms更新一下戰場 setInterval(function(){ updateFloor(); },100); //響應玩家的操作指令 window.onkeydown = function(eve){ switch(eve.keyCode){ case 38: case 87: myTank.y -= myTank.step; //Y坐標減小向上移動 break; case 40: case 83: myTank.y += myTank.step; //Y坐標增加向下移動 break; case 37: case 65: myTank.x -= myTank.step; //X坐標減小向左移動 break; case 39: case 68: myTank.x += myTank.step; //X坐標增加向右移動 } };
所有必要的說明都已經寫在了注釋中。這樣寫出來之后,我們發現坦克已經可以隨着玩家的按鍵上下左右移動了,但是還存在一點問題,坦克運動起來非常的生硬,不管向哪個方向動它的頭一直都是朝上的,我們必須在這個基礎上做以修改,下篇待續...