第6章 貪吃蛇的實現及代碼
講了不少東西了,老講理論的東西沒勁呀,我們不如先試着做一個小游戲吧。
作為我們的第一個游戲,當然是越簡單越好。《貪吃蛇》大家應該都玩過吧?我覺得我玩過的游戲中,她應該算是最簡單的一個了。好,就讓我們從做《貪吃蛇》開始,享受自己做游戲的樂趣吧。
由於這個游戲是本教程的第一個實際的游戲例子,我會講的比較詳細一些。請大家重點注意編程的思路和實現的方法,這些遠比代碼本身要重要。
一,蛇身的制作
蛇身由一系列方格組成,初始我們設定蛇身的長度是4,以后每吃到一次食物就增加1。
我們需要給組成蛇身的每一節一個坐標(可以保存在數組里),並設定一個變量var lenth=4;來存放蛇身的長度。
這樣我們要畫出蛇身的時候就很容易,只要用一個for循環for(i=0;i<lenth;i++)就可以根據坐標,一個一個格子地畫出蛇身。
二,蛇的前進
這個對於初學者來說,可能有一點點困難。因為在某一時間點,整個蛇的前進方向可能是不一樣的。
比如蛇頭正在向下運動,而尾巴可能還在向右。
如果你仔細觀察,你會發現,蛇的下一節的移動方向,就是她的前一節原來所在的地方。
比如蛇的第一節(也就是蛇頭)正在向右移動一格,那么她的第二節,就正在移動到第一節原來所在的地方;同樣,第三節正在移動到第二節原來所在的地方。
這樣就好辦了,我們只要讓下一節的坐標直接等於上一節當前的坐標就可以了。
function move() {for(i=(lenth-1);i>=1;i--) {worma[i]=worma[i-1]; wormb[i]=wormb[i-1]; } }
還剩第一節(蛇頭),因為第一節是沒有上一節的,所以第一節的坐標由蛇目前前進的方向(來自玩家的鍵盤輸入)計算得出。
三,食物的隨機出現
貪吃蛇的食物是隨機出現的。比如在我做的例子里,地圖的寬度是30,高度是20,那么我們可以使用random函數和floor函數來獲得一個寬30高20范圍內的隨機坐標。
(函數中的foodboolean用於判斷當前的food是否存在,如果不存在(=0),就生成新的food,如果已經存在(=1),就不新生成了。)
function getfood() {if(foodboolean==0) {fooda=Math.floor(Math.random()*20); foodb=Math.floor(Math.random()*30); foodboolean=1; }
四,判斷是不是咬到了自己
如果蛇頭(第一節)碰到了她自己身體的其他任意一節,那么這個時候,這2節的坐標是一樣的。我們可以根據這個來判斷是不是咬到自己了。
for(i=1;i<=lenth-1;i++) {if ((worma[i]==worma[0])&&(wormb[i]==wormb[0])) {crash=1;} }
五,自動前進和防止后退
鍵盤有4個方向,但是貪吃蛇在前進時,只有3個方向。我們只能讓她向前,或左轉,或右轉,而不能向后轉。所以如果玩家按了和當前前進方向相反的鍵盤按鍵,我們必須防止貪吃蛇反向移動。
方法是設定一個變量,direction,用於儲存當前的移動方向。如果玩家沒有新的輸入,那么貪吃蛇一直沿原來的方向走;如果有新的輸入,那么在判斷新的方向“合法”之后,存入direction變量,取代原來的方向。
例子中的direction變量用於儲存當前移動方向,dnext變量用於儲存下一個移動方向。
function fkeydown(kd) {if(Math.abs(direction-kd.keyCode)!=2) {dnext=kd.keyCode;}; }
六,其他的一些說明
1,由於javascript不支持二維數組,所以用了2個數組來儲存貪吃蛇的當前坐標,2個數組分別儲存x和y坐標。(當然,可以用1維數組模擬2維,我們下一章講地圖的時候會講到,但是這里沒必要用,直接用2個數組更簡單。)
2,有的人做《貪吃蛇》的時候喜歡把整個地圖做成一個2維數組,然后在每一個地圖的格子內儲存一些值,比如0代表這一格是空的,1代表這一格有被貪吃蛇占據,2代表這一格是食物等等。這當然也可以,但是這樣會比較復雜一點,也占用比較多的內存。因為貪吃蛇是最簡單的游戲,所以我們可以把她盡量簡化,我們可以完全不出現地圖數組,只依靠坐標來判斷。我們可以按照貪吃蛇和食物的坐標,而不是地圖的值,直接把它們畫出來;同樣,根據坐標來直接判斷貪吃蛇是否吃到食物,或者是否撞到她自己。
3,我下面的程序是今天新寫的,為了這個教程才寫的,沒有改過,也沒有經過簡化(其實有部分函數可以合並)。但是我覺得作為零基礎教程來說,每一個函數都很簡單,反而更容易理解,所以就不去簡化了。程序中的主要功能,已經在上面講過了,剩余部分很簡單。
4,本程序使用了結構化的方法,沒有使用面向對象的方法。以后我們在做一些較復雜的游戲時,會使用面向對象。
5,本程序經測試,可以正常使用。
轉載的話,請注明出處,謝謝
CSDN博客: http://blog.csdn.net/trackstatic/
博客園博客:http://www.cnblogs.com/phyy/ 地球生活eev
(連本段一起復制,就算是注明出處了)
下面是源程序:
<html> <head> <title>greedy snake from eev</title> <script> var x=0; var y=0; var i=0; var a=0; var b=0; var fooda=0; var foodb=0; var foodboolean=0; var lenth=4; var timer=0; var direction=39; var dnext=39; var crash=0; var worma=new Array(); var wormb=new Array(); worma[0]=10; wormb[0]=1; worma[1]=9; wormb[1]=1; worma[2]=8; wormb[2]=1; worma[3]=7; wormb[3]=1; var nexta=10; var nextb=1; function getfood() {if(foodboolean==0) {fooda=Math.floor(Math.random()*20); foodb=Math.floor(Math.random()*30); foodboolean=1; }; } function next() {direction=dnext; if (direction==37) {fleft();} else if(direction==38) {fup();} else if(direction==39) {fright();} else if(direction==40) {fdown();} } function eat() {lenth=lenth+1; move(); foodboolean=0; } function eatormove() {if((foodboolean==1)&&(nexta==fooda)&&(nextb==foodb)) {eat();} else {move();}; } function move() {for(i=(lenth-1);i>=1;i--) {worma[i]=worma[i-1]; wormb[i]=wormb[i-1]; }; worma[0]=nexta; wormb[0]=nextb; } function fleft() {if(wormb[0]>=1) nextb=wormb[0]-1; else nextb=29; } function fright() {if(wormb[0]<29) nextb=wormb[0]+1; else nextb=0; } function fup() {if(worma[0]>=1) nexta=worma[0]-1; else nexta=19; } function fdown() {if(worma[0]<19) nexta=worma[0]+1; else nexta=0; } function fkeydown(kd) {if(Math.abs(direction-kd.keyCode)!=2) {dnext=kd.keyCode;}; } function draw() {ctx.clearRect(0,0,1200,800); for(i=0;i<=lenth-1;i++) {x=wormb[i]*40; y=worma[i]*40; ctx.fillStyle="red"; ctx.fillRect(x,y,40,40); } if(foodboolean==1) {ctx.fillStyle="green"; x=foodb*40; y=fooda*40; ctx.fillRect(x,y,40,40); } } function refresh() {timer=timer+1; for(i=1;i<=lenth-1;i++) {if ((worma[i]==worma[0])&&(wormb[i]==wormb[0])) {crash=1;}; } if ((timer%5==1)&&crash==0) {next(); eatormove(); }; draw(); getfood(); } </script> </head> <body onkeydown="fkeydown(event)"> <canvas id="canvas01" width="1200" height="800" style="background-color:black"> </canvas> <script> var cvs=document.getElementById("canvas01"); var ctx=cvs.getContext("2d"); ctx.fillStyle="red"; var intval=setInterval("refresh();",100); </script> </body> </html>