實驗7 貪吃蛇
簡介
1976年,Gremlin平臺推出了一款經典街機游戲Blockade。游戲中,兩名玩家分別控制一個角色在屏幕上移動,所經之處砌起圍欄。角色只能向左、右方向90°轉彎,游戲的目標是讓對方先撞上邊界或圍欄。聽起來有些復雜,其實就是兩條每走一步都會長大的貪吃蛇比誰堅持的時間長,玩家要做的就是避免撞上障礙物和越來越長的身體。更多照片、視頻可以看 GamesDBase的介紹。
Blockade很受歡迎,類似的游戲先后出現在 Atari 2600、TRS-80、蘋果2等早期游戲機、計算機上。但真正讓這種游戲形式紅遍全球的還是21年后隨諾基亞手機走向世界的貪吃蛇游戲——Snake,如圖1-24所示。

圖1-24 諾基亞貪吃蛇游戲
貪吃蛇算法
首先用Jscex把整個游戲的Loop搭建起來:
var moveAsync=eval(Jscex.compile("async", function (p) { while (true) { //... //code here //... $await(Jscex.Async.sleep(100)); } }));
然后把相關的算法加進去。首先是蛇的移動算法,采用每一個時間間隔去掉尾部一個節點,并且向蛇的頭部運動方向增加一個節點的方法。
if (direction=="right") { p.push({ x: p[p.length-1].x+1, y: p[p.length-1].y }); } if (direction=="left") { p.push({ x: p[p.length-1].x-1, y: p[p.length-1].y }); } if (direction=="up") { p.push({ x: p[p.length-1].x, y: p[p.length-1].y-1 }); } if (direction=="down") { p.push({ x: p[p.length-1].x, y: p[p.length-1].y+1 }); }
蛇的頭部運動方向共有4個,蛇在運動時都是往頭部運動方向增加一個節點,因為當蛇吃到食物時節點增加一個,所以不是每次都會去掉尾部節點,在判斷到蛇沒有吃到食物時去掉尾部的一個節點:
if (p[p.length-1].x==foodPositions.x && p[p.length-1].y==foodPositions.y) { foodPositions.x=-1; foodPositions.y=-1; } else { clearColor(p[0]); p.shift(); }
然后是隨機生成蛇的食物(這里可以加一個遞歸,如果生成的位置和蛇的位置重疊,則重新生成,一直遞歸下去,直到沒有重疊)。
var MR=Math.random; function drawSnakeAndFood(p) { if (foodPositions.x==-1 && foodPositions.y==-1) { foodPositions.x=MR() * 40 | 0; foodPositions.y=MR() * 40 | 0; } cxt.fillStyle=randomColor(); cxt.fillRect(foodPositions.x * width, foodPositions.y * height, height, width); for (i=0; i < p.length; i++) { cxt.fillStyle="#D1EEEE"; cxt.fillRect(p[i].x * width, p[i].y * height, height, width); } }
GameOver的判定:
if (p[p.length-1].x < 0 || p[p.length-1].x > 39 || p[p.length-1].y < 0 || p[p.length-1].y > 39) { gameOver=true; } if (isInSnake(p, { x: p[p.length-1].x, y: p[p.length-1].y })) { gameOver=true; }
GameOver分兩種情況,一種是撞到墻壁和障礙物,一種是撞到自己的身體,代碼如下:
var moveAsync=eval(Jscex.compile("async", function (p) { while (true) { if (p[p.length-1].x < 0 || p[p.length-1].x > 39 || p[p.length-1].y < 0 || p[p.length-1].y > 39) { gameOver=true; } if (p[p.length-1].x==foodPositions.x && p[p.length-1].y==foodPositions.y) { foodPositions.x=-1; foodPositions.y=-1; } else { clearColor(p[0]); p.shift(); } if (direction=="right") { p.push({ x: p[p.length-1].x+1, y: p[p.length-1].y }); } if (direction=="left") { p.push({ x: p[p.length-1].x-1, y: p[p.length-1].y }); } if (direction=="up") { p.push({ x: p[p.length-1].x, y: p[p.length-1].y-1 }); } if (direction=="down") { p.push({ x: p[p.length-1].x, y: p[p.length-1].y+1 }); } drawSnakeAndFood(p); if (gameOver) { drawGameOver(); document.getElementById("btnReset").disabled="; break; } $await(Jscex.Async.sleep(100)); } }));
運行效果如圖1-25所示。

圖1-25 Canvas中的貪吃蛇游戲