我原来用C语言,借助curses库实现了linux 终端下的贪吃蛇游戏。
这个javascript版本的贪吃蛇是http://www.veryhuo.com/game/tanchishe.html 的学习笔记,实现的原理和C版本基本一样。
这里最为重要的是用gridElems这个二维数组来表示20*20的格子(实际上是html的td元素),通过gridElems[x][y].className控制td元素的css. 再者就是snake运动中的判断。
--------------------
1.怎样表示一条snake
用一个二维数组存snake的各个点(x,y),同时标记这些点(x,y)为“cover”,这是用于以后检查snake的头是否撞到了snake的body。
//initialize 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]);
gridElems[x][y].className = "cover"; //标记snake body
}
}
2.用js画出格子
用document.createElent()方法创建出table->tr->td, 然后用document.appendChild()方法追加到id为“snakewrap”的元素上:
//initialize grid
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); //这里很重要,通过一个二维数组的元素来表示td,通过className控制td的color
}
tbody.appendChild(col);
}
table.appendChild(tbody);
document.getElementById("snakewrap").appendChild(table);
}
部分CSS文件:
td {
width:20px;
height:20px;
background:#f4f4f4/* td background color*/
}
/*snake body color*/
.cover { background:#39c;}
/*food color*/
.food {background:#093}
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];
gridElems[x][y].className = name;
}
4.方向键按下动作事件监听:
允许左上右下这4个按键来改变snake的运动方向,注意,如果方向相反的话,不生效。
对于键盘上的每一个按键,都有一个key cord,我的这篇博客记录了javascript的key cord,可看到:
left arrow | 37 |
up arrow | 38 |
right arrow | 39 |
down arrow | 40 |
//keyboard event listener
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的身体(即gridElems[headX][headY].className == "cover"时),如果碰到,则游戏结束。
3)判断“头”当前的位置是不是食物(即gridElems[headX][headY].className == "food"), 如果头元素的carrier不是食物,则让snake的尾巴pop出来;如果是,则让当前位置的携带信息gridElems[headX][headY].className = false
4 )向数组的开头添加一个元素-->从而实现了“视觉上”的snake移动(或吃食物body增长)的效果
function step() {
//把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 || gridElems[headX][headY].className == "block" || gridElems[headX][headY].className == "cover") {
$("say").innerText = "Game Over.";
$("start").removeAttribute("disabled");
$("start").style.color = "#000";
window.clearInterval(snakeTimer);
return;
}
var lastX = snake[snake.length-1][0], lastY = snake[snake.length-1][1];
//如果头元素的carrier不是食物,则让snake的尾巴pop出来
if(gridElems[headX][headY].className != "food") {
gridElems[lastX][lastY].className = "";
snake.pop();
}
else {
gridElems[lastX][lastY].className = "";
addObject("food");
}
//向数组的开头添加一个元素-->从而实现了“视觉上”的snake移动(或吃食物body增长)的效果
snake.unshift([headX,headY]);
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.参考: http://www.veryhuo.com/game/tanchishe.html
8.玩玩:我的simple and stupid snake game
效果图(CSS我借用了下最近很火的2048):