程序包括一个html文件:snake.html和一个js文件:snake.js
snake.html:
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta
http-equiv="Content-Type" content="text/html; charset=utf-8"
/>
<title>JavaScript简单贪吃蛇</title>
<script
type="text/javascript" src="snake.js"></script>
<script
type="text/javascript" >
$s(function(){
$s.SnakeContext.init();
});
</script>
</head>
<body>
<div
id="headLocation"></div>
<div
id="keyup"></div>
</body>
</html>
snake.js:
/*
* JavaScript简单贪吃蛇.基本面向对象.
* 规则:
*
1.没有墙,左与右连接,上与下连接.
* 2.当蛇头碰撞到自身时死亡.
* 兼容性:
*
完全支持Firefox,Chrome
* 基本支持IE(除了调试部分)
*
* 作者:pcenshao
*
转载请注明来自:
* http://blog.youkuaiyun.com/pywepe
*
http://pcenshao.taobao.com
*/
(function(){
$s =
function(){
if(arguments.length == 1){
if(typeof
arguments[0] == "string"){
return
document.getElementById(arguments[0]);
}else if(typeof
arguments[0] == "function"){
window.onload =
arguments[0];
}
}
};
$s.UNIT_WIDTH = 10; // 单元的宽度
$s.UNIT_HEIGHT = 10;
$s.PANEL_WIDTH
= 30; // 逻辑宽度
$s.PANEL_HEIGHT = 20; // 逻辑高度
$s.STEP = 250 ;
// 每一步的时间
$s.HEAD_COLOR = "red"; // 蛇头颜色
$s.BODY_COLOR = "black";
// 蛇体颜色
/*
* 食物的颜色
*/
$s.COLORS =
["blue","green","#494e8f","#905d1d","#845538","#77ac98","#8552a1"];
/*
* 调试相关
* $s.DEBUG 调试信息显示开关
*
$s.KEY_UP_DIR_ID 监视方向键的结点id,若不存在,则不显示
* $s.HEAD_LOCATION_ID
监视蛇头位置的结点id,若不存在,则不显示
*/
$s.DEBUG = false;
$s.KEY_UP_DIR_ID = "keyup";
$s.HEAD_LOCATION_ID = "headLocation";
$s.Dir = { // 代表方向,强制以$s.Dir.UP方法调用,避免参数错误
UP : {},
DOWN : {},
LEFT : {},
RIGHT : {},
NONE :
{}
};
$s.State = { // 代表状态
STOP : {},
RUNNGIN : {},
PAUSE : {}
};
$s.Unit = function(){ // 一个单元格,用MVC的眼光看,Unit是模型,UnitView是视图
this.x =
0;
this.y = 0;
this.view = new $s.UnitView();
this.view.unit = this;
this.color = $s.BODY_COLOR;
};
$s.Unit.prototype.repaint = function(){
if(this.view != null){
this.view.repaint(); // 通知重绘
}
};
$s.Snake = function(){
this.units = [];
};
$s.Snake.prototype.init = function(dir,count){
var x
= 5;
var y = 5;
for(var i = 0 ; i < count ; i
++){
var u = new $s.Unit();
u.x = x ;
u.y = y ++;
this.units.push(u);
if(i ==
(count - 1 )){
u.color = $s.HEAD_COLOR;
}
u.repaint();
}
};
$s.Snake.prototype.crash = function(x,y){ // 传入头部的位置,返回true表示碰撞自身
for(var i = this.units.length - 2 ; i >= 0 ; i --){ // 不包括头自身
var u = this.units[i];
if(u.x == x && u.y == y){
return true;
}
}
return
false;
};
$s.Snake.prototype.go = function(){
// 判断前方是否有食物
// 是否撞墙
var _x = 0 , _y = 0;
var
head = this.units[this.units.length - 1];
_x = head.x;
_y
= head.y;
var dir = $s.SnakeContext.dir;
if(this.crash(_x,_y)){ // 判断是否碰撞到自身
$s.SnakeContext.stop();
$s.SnakeContext.ondead(); //
触发dead事件
return;
}
if(dir ==
$s.Dir.LEFT){
_x --;
}else if(dir ==
$s.Dir.RIGHT){
_x ++;
}else if(dir ==
$s.Dir.UP){
_y --;
}else if(dir == $s.Dir.DOWN){
_y ++;
}
// 实现左右连接,上下连接
if(_x >= $s.PANEL_WIDTH){
_x = 0;
}
if(_x < 0){
_x = $s.PANEL_WIDTH - 1;
}
if(_y >= $s.PANEL_HEIGHT){
_y = 0;
}
if(_y < 0){
_y = $s.PANEL_HEIGHT - 1;
}
var h = new $s.Unit(); // 新头
if($s.SnakeContext.hasFood(_x,_y)){ // 下一步碰到食物
this.eat(_x,_y);
head = this.units[this.units.length - 1]; //
因为eat方法可以改变头部,所以重新获取
_x = head.x;
_y =
head.y;
if(dir == $s.Dir.LEFT){
_x --;
}else if(dir == $s.Dir.RIGHT){
_x ++;
}else if(dir == $s.Dir.UP){
_y
--;
}else if(dir == $s.Dir.DOWN){
_y ++;
}
head.color = $s.HEAD_COLOR;
head.repaint();
var oldHead = this.units[this.units.length -
2];
oldHead.color = $s.BODY_COLOR;
oldHead.repaint();
return;
}
var tail = this.units.shift();
$s.NodePool.releaseNode(tail);
h.x = _x;
h.y = _y;
this.units.push(h);
for(var i = this.units.length - 1; i
>= 0; i --){
var u = this.units[i];
if(i ==
(this.units.length - 1)){ // 头
u.color = $s.HEAD_COLOR;
}else{
u.color = $s.BODY_COLOR;
}
u.repaint();
}
};
$s.Snake.prototype.eat = function(x,y){
var food =
$s.SnakeContext.food;
if(food != null){
food.alive =
false;
this.units.push(food.unit);
$s.SnakeContext.oneat();
}else{
alert("error:no food
on (" + x + "," + y + ")");
}
}
/*
* 随机数产生器,提供简便的方法
*/
$s.Random = {
randomNumber : function(lower,upper){ // 返回区间[lower,upper]的整数
var choices = upper - lower + 1;
return Math.floor(Math.random()
* choices + lower); // value = Math.floor(Math.random() * 可能值的个数 +
第一个可能的值)
},
randomLocation : function(maxX,maxY){
var x = $s.Random.randomNumber(0,maxX);
var y =
$s.Random.randomNumber(0,maxY);
return {x:x,y:y};
}
};
$s.Food = function(x,y){ // 代表食物,由一个Unit表示
this.unit = new $s.Unit();
this.unit.x = x;
this.unit.y =
y;
var color = $s.COLORS[$s.Random.randomNumber(0,$s.COLORS.length -
1)];
this.unit.color = color;
this.alive = true;
this.unit.repaint();
};
$s.Food.prototype.locateOn =
function(x,y){
return this.unit.x == x && this.unit.y ==
y;
};
/*
* HTML结点池,主要目的是提高效率
*
因为snake的移动是通过删除尾部结点并向头部添加结点实现的,
*
在这个过程中会有大量的结点创建操作,为了操作效率,所以对结点进行池化管理.
* 尾部的结点不删除,而是隐藏,需要结点时可以重用之
*/
$s.NodePool = {
nodes : []
};
$s.NodePool._findHideNode = function(){ // 查找隐藏的div结点
for(var i = 0 ; i < this.nodes.length ; i ++){
var n =
this.nodes[i];
if(n.style.display == "none"){
return n;
}
}
return null;
};
$s.NodePool.createNode = function(){
var pooledNode =
this._findHideNode();
if(pooledNode != null){
return
pooledNode;
}else{
var newNode =
document.createElement("div");
this.nodes.push(newNode);
return newNode;
}
};
$s.NodePool.releaseNode =
function(node){
if(node != undefined && node != null){
if(node instanceof $s.Unit){
var view =
node.view;
if(view != null){
var div =
view.node;
div.style.display = "none";
}
}
}
}
$s.UnitView = function(){ // Unit的视图
this.unit = null;
this.node = null;
};
$s.UnitView.prototype.repaint =
function(){
if(this.node == null){ // 初始化
var tag =
$s.NodePool.createNode();
tag.style.width = $s.UNIT_WIDTH +
"px";
tag.style.height = $s.UNIT_HEIGHT + "px";
tag.style.borderStyle = "dotted";
tag.style.borderWidth =
"1px";
tag.style.borderColor = "white";
tag.style.margintLeft = "1px";
tag.style.marginRight =
"1px";
tag.style.marginTop = "1px";
tag.style.marginBottom = "1px";
tag.style.backgroundColor =
this.unit.color; // 颜色由模型Unit指定
tag.style.position = "absolute";
//容器的position指定为relative,孩子的position指定为absolute时,表示孩子相对容器绝对定位.
tag.style.display = "block"; // 重要,因为从NodePool取现的结点是隐藏状态的
var x = this.unit.x * $s.UNIT_WIDTH;
var
y = this.unit.y * $s.UNIT_HEIGHT;
tag.style.top = y + "px";
tag.style.left = x + "px";
this.node =
tag;
$s.SnakeContext.panelView.append(this);
}else{
var tag = this.node;
var x
= this.unit.x * $s.UNIT_WIDTH;
var y = this.unit.y *
$s.UNIT_HEIGHT;
tag.style.top = y + "px";
tag.style.left = x + "px";
tag.style.backgroundColor =
this.unit.color;
}
};
$s.PanelView =
function(){ // 整个游戏区域,包括按钮区
var panel =
document.createElement("div");
panel.style.width = ($s.PANEL_WIDTH *
$s.UNIT_WIDTH ) + "px";
panel.style.height = ($s.PANEL_HEIGHT *
$s.UNIT_HEIGHT ) + "px";
panel.style.borderStyle = "dotted";
panel.style.borderColor = "red";
panel.style.borderWidth =
"1px";
panel.style.marginLeft = "auto";
panel.style.marginRight = "auto";
panel.style.marginTop =
"50px";
panel.style.position = "relative"; //
容器的position指定为relative,孩子的position指定为absolute时,表示孩子相对容器绝对定位.
panel.style.marginBottom = "auto";
this.node = panel;
document.body.appendChild(panel);
var len =
document.createElement("div");
len.style.marginLeft = "auto";
len.style.marginRight = "auto";
len.style.marginBottom =
"20px";
len.style.color = "gray";
len.style.fontSize =
"12px";
len.innerHTML = "长度:";
document.body.appendChild(len);
$s.SnakeContext._len = len;
var startBn = document.createElement("button");
startBn.innerHTML = "开始";
startBn.style.marginLeft = "10px";
startBn.onclick = function(){
$s.SnakeContext.run();
};
$s.SnakeContext._startBn = startBn;
document.body.appendChild(startBn);
var pauseBn =
document.createElement("button");
pauseBn.innerHTML = "暂停";
pauseBn.style.marginLeft = "10px";
pauseBn.onclick =
function(){
$s.SnakeContext.pause();
};
$s.SnakeContext._pauseBn = pauseBn;
document.body.appendChild(pauseBn);
/*
var
stopBn = document.createElement("button");
stopBn.innerHTML =
"停止";
stopBn.style.marginLeft = "10px";
stopBn.onclick =
function(){
$s.SnakeContext.stop();
};
$s.SnakeContext._stopBn = stopBn;
document.body.appendChild(stopBn);
*/
var
restartBn = document.createElement("button");
restartBn.innerHTML =
"重新开始";
restartBn.style.marginLeft = "10px";
restartBn.onclick = function(){
window.location.href =
window.location.href;
};
$s.SnakeContext._restartBn =
restartBn;
document.body.appendChild(restartBn);
var line = document.createElement("div");
line.style.height =
"10px";
document.body.appendChild(line);
var
span = document.createElement("span");
span.style.color =
"gray";
span.style.fontSize = "12px";
span.innerHTML =
"调试";
document.body.appendChild(span);
var
debug = document.createElement("input");
debug.type = "checkbox";
debug.checked = $s.DEBUG;
debug.onchange =
function(){
$s.SnakeContext.setDebug(debug.checked);
};
document.body.appendChild(debug);
};
$s.PanelView.prototype.append = function(unitView){
try{
this.node.appendChild(unitView.node);
}catch(e){
alert(e);
}
};
/*
* 全局环境类,代表应用
* 约定以_开头的成员为私有成员
*
启动程序的方法:
* window.onload = function(){
*
$s.SnakeContext.init();
* }
*/
$s.SnakeContext = {
dir : $s.Dir.NONE,
state : $s.State.STOP,
goTimer : null,
run : function(){
if(this.state != $s.State.RUNNGIN){
this.state =
$s.State.RUNNGIN;
this.goTimer =
window.setInterval(function(){
$s.SnakeContext.updateFood();
$s.SnakeContext.snake.go();
},$s.STEP);
}
},
stop : function(){
this._setState($s.State.STOP);
},
pause :
function(){
this._setState($s.State.PAUSE);
},
_setState : function(s){
if(this.state != s &&
this.goTimer != null){
window.clearInterval(this.goTimer);
this.goTimer =
null;
this.state = s;
}
},
getFood : function(x,y){
for(var f in
this.foods){
if(f.x == x && f.y == y){
return f;
}
}
return null;
},
init : function(){
this.panelView = new $s.PanelView();
this.snake = new
$s.Snake();
this.dir = $s.Dir.DOWN;
this.snake.init($s.Dir.UP,3);
this._len.innerHTML = "长度:" +
3;
document.body.onkeyup = function(e){
var code = null;
if(window.event){ // fuck的IE
code = window.event.keyCode;
}else{
code = e.keyCode;
}
var str = "";
var oldDir =
$s.SnakeContext.dir;
switch(code){
case 37: // left
if($s.SnakeContext.dir !=
$s.Dir.RIGHT){
$s.SnakeContext.dir =
$s.Dir.LEFT;
}
str
= "left";
break;
case 38 : //
up
if($s.SnakeContext.dir != $s.Dir.DOWN){
$s.SnakeContext.dir = $s.Dir.UP;
}
str = "up";
break;
case 39: // right
if($s.SnakeContext.dir != $s.Dir.LEFT){
$s.SnakeContext.dir = $s.Dir.RIGHT;
}
str = "right";
break;
case 40: // down
if($s.SnakeContext.dir != $s.Dir.UP){
$s.SnakeContext.dir = $s.Dir.DOWN;
}
str = "down";
break;
}
if($s.SnakeContext.dir !=
oldDir){
if($s.DEBUG){
var v =
$s($s.KEY_UP_DIR_ID);
if(v){
v.innerHTML = "方向键:" + str;
}
}
if($s.SnakeContext.goTimer != null){
window.clearInterval($s.SnakeContext.goTimer);
$s.SnakeContext.goTimer = null;
}
$s.SnakeContext.snake.go();
$s.SnakeContext.goTimer = window.setInterval(function(){
$s.SnakeContext.updateFood();
$s.SnakeContext.snake.go();
},$s.STEP);
}
};
var loc =
$s.Random.randomLocation($s.PANEL_WIDTH - 1, $s.PANEL_HEIGHT - 1);
this.food = new $s.Food(loc.x,loc.y);
},
snake : null,
foods : [],
panelView : null,
food : null,
updateFood : function(){
if(this.food.alive){ // 当前Food还存活
return;
}
var loc = null;
do{
//
随机产生一个点,直到不Snake重叠
loc =
$s.Random.randomLocation($s.PANEL_WIDTH - 1,$s.PANEL_HEIGHT - 1);
}while(this.overlap(loc));
this.food = new
$s.Food(loc.x,loc.y);
},
overlap :
function(loc){ // 检查是否与Snake重叠,当重叠时返回true
var x = loc.x;
var y = loc.y;
for(var i = 0 ; i <
this.snake.units.length ; i ++ ){
var u =
this.snake.units[i];
if(u.x == x && u.y == y){
return true;
}
}
return false;
},
hasFood : function(x,y){
if($s.DEBUG){
var xt =
$s($s.HEAD_LOCATION_ID);
if(xt){
xt.innerHTML = "头部位置:(" + x + "," + y + ")";
}
}
return
this.food.locateOn(x,y);
},
setDebug :
function(enable){
if(enable != $s.DEBUG){
$s.DEBUG = enable;
if($s.DEBUG){ // 显示
var i = $s($s.KEY_UP_DIR_ID);
$s.SnakeContext._show(i);
i =
$s($s.HEAD_LOCATION_ID);
$s.SnakeContext._show(i);
}else{ // 隐藏
var i =
$s($s.KEY_UP_DIR_ID);
$s.SnakeContext._hide(i);
i = $s($s.HEAD_LOCATION_ID);
$s.SnakeContext._hide(i);
}
}
},
_show : function(tag){
if(tag){
tag.style.display = "block";
}
},
_hide : function(tag){
if(tag){
tag.style.display = "none";
}
},
ondead : function(){ // Snake死亡时回调
if(this._startBn){
this._startBn.disabled = true;
}
if(this._pauseBn){
this._pauseBn.disabled = true;
}
if(this._stopBn){
this._stopBn.disabled = true;
}
alert("挂了");
},
oneat : function(){ //
Snake长度增加时回调
this._len.innerHTML = "长度:" +
this.snake.units.length;
},
_startBn : null,
_pauseBn : null,
_stopBn : null,
_restartBn : null,
_len : null
};
})();
一款使用HTML和JavaScript实现的简单贪吃蛇游戏,支持方向键控制蛇的移动方向,蛇身碰撞自身则游戏结束。兼容Firefox、Chrome浏览器。
289

被折叠的 条评论
为什么被折叠?



