用一个二维数组存snake的各个点(x,y),同时标记这些点(x,y)为“cover”,这是用于以后检查snake的头是否撞到了snake的body。
//初始化 snake function initSnake() { var pointer = randomPointer(len-1, len-1, WIDTH/2); for(var i = 0; i < len; i++) { var x = pointer[0] - i, y = pointer[1]; snake.push([x,y]); carrier[x][y] = "cover"; //标记snake body } }2.用js画出格子
用document.createElent()方法创建出table->tr->td, 然后用document.appendChild()方法追加到id为“snakewrap”的元素上:
//初始化格子 function initGrid() { var body = document.getElementsByTagName("body")[0]; var table = document.createElement("table"), tbody = document.createElement("tbody") for(var j = 0; j < HEIGHT; j++) { var col = document.createElement("tr"); for(var i = 0; i < WIDTH; i++) { var row = document.createElement("td"); gridElems[i][j] = col.appendChild(row); } tbody.appendChild(col); } table.appendChild(tbody); document.getElementById("snakewrap").appendChild(table); }3.生成食物的随机坐标
function randomPointer(startX,startY,endX,endY) { startX = startX || 0; startY = startY || 0; endX = endX || WIDTH; endY = endY || HEIGHT; var p = [], x = Math.floor(Math.random()*(endX - startX)) + startX, y = Math.floor(Math.random()*(endY - startY)) + startY; //如果(x,y)有物体,则重新生成坐标 if(carrier[x][y]) { return randomPointer(startX,startY,endX,endY); } p[0] = x; p[1] = y; return p; }添加新的食物:
//addObject("food") function addObject(name) { var p = randomPointer(); //get random position var x = p[0]; var y = p[1]; carrier[x][y] = name; gridElems[x][y].className = name; }4.方向键按下动作事件监听:
允许左上右下这4个按键来改变snake的运动方向,注意,如果方向相反的话,不生效。
对于键盘上的每一个按键,都有一个key cord,我的这篇博客记录了javascript的key cord,可看到:
left 37
up 38
right 39
down 40
//键盘事件侦听 function attachEvents(e) { e = e || event; directkey = Math.abs(e.keyCode - directkey) != 2 && e.keyCode > 36 && e.keyCode < 41 ? e.keyCode : directkey; return false; }5.贪吃蛇的核心--判断
每次判断(即judge()函数每运行一次-->这里用到了setInterval()方法),都要先把snake的“头”节点保存下来,然后做判断
1)判断方向,根据方向调整“头”的坐标(由于有setInterval()方法,系统会每个若个毫秒就运行一次judge()函数,确保用户按下方向键后能够该表方向)
2)判断“头”是否撞到墙,或碰到snake的身体(即carrier[headX][headY] == "cover"时),如果碰到,则游戏结束。
3)判断“头”当前的位置是不是食物(即carrier[headX][headY] == "food"), 如果头元素的carrier不是食物,则让snake的尾巴pop出来;如果是,则让当前位置的携带信息carrier[headX][headY] = false
4 )向数组的开头添加一个元素-->从而实现了“视觉上”的snake移动(或吃食物body增长)的效果
function judge() { //把snake的“头”位置暂存起来 var headX = snake[0][0], headY = snake[0][1]; switch(directkey) { case 37: headX -= 1; break; //left case 38: headY -= 1; break; //up case 39: headX += 1; break //right case 40: headY += 1; break; //down } //碰到边界(block),或头碰到身体(cover),则结束游戏 if(headX >= WIDTH || headX < 0 || headY >= HEIGHT || headY < 0 || carrier[headX][headY] == "block" || carrier[headX][headY] == "cover" ) { $("say").innerText = "Game Over."; $("start").removeAttribute("disabled"); $("start").style.color = "#000"; window.clearInterval(snakeTimer); return; } //如果头元素的carrier不是食物,则让snake的尾巴pop出来 if(carrier[headX][headY] != "food") { var lastX = snake[snake.length-1][0], lastY = snake[snake.length-1][1]; carrier[lastX][lastY] = false; gridElems[lastX][lastY].className = ""; snake.pop(); }else { carrier[headX][headY] = false; addObject("food"); //吃掉食物,然后调用addObject("food"),重新生成食物 } //向数组的开头添加一个元素-->从而实现了“视觉上”的snake移动(或吃食物body增长)的效果 snake.unshift([headX,headY]); carrier[headX][headY] = "cover"; gridElems[headX][headY].className = "cover"; len = snake.length; }setInterval()函数(使得上面的judge()函数每隔300ms就运行一次):
function run_run_run() { if(snakeTimer) { window.clearInterval(snakeTimer); } snakeTimer = window.setInterval("judge()", Math.floor(300)); }6.onload 运行
onload 事件会在页面或图像加载完成后立即发生:
window.onload = function(){ initGrid(); document.onkeydown = attachEvents; //监听keydown事件 $("start").onclick = function (e) { len = 3; //snake的初始长度 directkey = 39; //right snake = new Array(); initSnake(); addObject("food"); run_run_run(); //让start按钮失效 $("start").setAttribute("disabled",true); $("start").style.color = "#aaa"; } }7.我们看下最终效果: