JavaScript简单贪吃蛇,基本面向对象

lzn 2011-07-31

没有写博客的习惯,这篇算心血来潮,算篇近几天编写的小程序纪实.

以编写此程序的方式结束Javascript的本阶段的学习.编写的目的在于熟悉javascript的编程方式,包括代码风格,面向对象的运用等.

回到程序,说说Snake的移动的实现方法.其实很简单,向头部添加Unit,然后删除尾部.其他,参见注释.

作者:pcenshao

转载请注明来自:

http://blog.csdn.net/pywepe

程序包括一个html文件:snake.html和一个js文件:snake.js

snake.html:

<!DOCTYPEhtmlPUBLIC"-//W3C//DTDXHTML1.0Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<htmlxmlns="http://www.w3.org/1999/xhtml">

<head>

<metahttp-equiv="Content-Type"content="text/html;charset=utf-8"/>

<title>JavaScript简单贪吃蛇</title>

<scripttype="text/javascript"src="snake.js"></script>

<scripttype="text/javascript">

$s(function(){

$s.SnakeContext.init();

});

</script>

</head>

<body>

<divid="headLocation"></div>

<divid="keyup"></div>

</body>

</html>

snake.js:

/*

*JavaScript简单贪吃蛇.基本面向对象.

*规则:

*1.没有墙,左与右连接,上与下连接.

*2.当蛇头碰撞到自身时死亡.

*兼容性:

*完全支持Firefox,Chrome

*基本支持IE(除了调试部分)

*

*作者:pcenshao

*转载请注明来自:

*http://blog.csdn.net/pywepe

*http://pcenshao.taobao.com

*/

(function(){

$s=function(){

if(arguments.length==1){

if(typeofarguments[0]=="string"){

returndocument.getElementById(arguments[0]);

}elseif(typeofarguments[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){

varx=5;

vary=5;

for(vari=0;i<count;i++){

varu=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(vari=this.units.length-2;i>=0;i--){//不包括头自身

varu=this.units[i];

if(u.x==x&&u.y==y){

returntrue;

}

}

returnfalse;

};

$s.Snake.prototype.go=function(){

//判断前方是否有食物

//是否撞墙

var_x=0,_y=0;

varhead=this.units[this.units.length-1];

_x=head.x;

_y=head.y;

vardir=$s.SnakeContext.dir;

if(this.crash(_x,_y)){//判断是否碰撞到自身

$s.SnakeContext.stop();

$s.SnakeContext.ondead();//触发dead事件

return;

}

if(dir==$s.Dir.LEFT){

_x--;

}elseif(dir==$s.Dir.RIGHT){

_x++;

}elseif(dir==$s.Dir.UP){

_y--;

}elseif(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;

}

varh=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--;

}elseif(dir==$s.Dir.RIGHT){

_x++;

}elseif(dir==$s.Dir.UP){

_y--;

}elseif(dir==$s.Dir.DOWN){

_y++;

}

head.color=$s.HEAD_COLOR;

head.repaint();

varoldHead=this.units[this.units.length-2];

oldHead.color=$s.BODY_COLOR;

oldHead.repaint();

return;

}

vartail=this.units.shift();

$s.NodePool.releaseNode(tail);

h.x=_x;

h.y=_y;

this.units.push(h);

for(vari=this.units.length-1;i>=0;i--){

varu=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){

varfood=$s.SnakeContext.food;

if(food!=null){

food.alive=false;

this.units.push(food.unit);

$s.SnakeContext.oneat();

}else{

alert("error:nofoodon("+x+","+y+")");

}

}

/*

*随机数产生器,提供简便的方法

*/

$s.Random={

randomNumber:function(lower,upper){//返回区间[lower,upper]的整数

varchoices=upper-lower+1;

returnMath.floor(Math.random()*choices+lower);//value=Math.floor(Math.random()*可能值的个数+第一个可能的值)

},

randomLocation:function(maxX,maxY){

varx=$s.Random.randomNumber(0,maxX);

vary=$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;

varcolor=$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){

returnthis.unit.x==x&&this.unit.y==y;

};

/*

*HTML结点池,主要目的是提高效率

*因为snake的移动是通过删除尾部结点并向头部添加结点实现的,

*在这个过程中会有大量的结点创建操作,为了操作效率,所以对结点进行池化管理.

*尾部的结点不删除,而是隐藏,需要结点时可以重用之

*/

$s.NodePool={

nodes:[]

};

$s.NodePool._findHideNode=function(){//查找隐藏的div结点

for(vari=0;i<this.nodes.length;i++){

varn=this.nodes[i];

if(n.style.display=="none"){

returnn;

}

}

returnnull;

};

$s.NodePool.createNode=function(){

varpooledNode=this._findHideNode();

if(pooledNode!=null){

returnpooledNode;

}else{

varnewNode=document.createElement("div");

this.nodes.push(newNode);

returnnewNode;

}

};

$s.NodePool.releaseNode=function(node){

if(node!=undefined&&node!=null){

if(nodeinstanceof$s.Unit){

varview=node.view;

if(view!=null){

vardiv=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){//初始化

vartag=$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取现的结点是隐藏状态的

varx=this.unit.x*$s.UNIT_WIDTH;

vary=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{

vartag=this.node;

varx=this.unit.x*$s.UNIT_WIDTH;

vary=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(){//整个游戏区域,包括按钮区

varpanel=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);

varlen=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;

varstartBn=document.createElement("button");

startBn.innerHTML="开始";

startBn.style.marginLeft="10px";

startBn.onclick=function(){

$s.SnakeContext.run();

};

$s.SnakeContext._startBn=startBn;

document.body.appendChild(startBn);

varpauseBn=document.createElement("button");

pauseBn.innerHTML="暂停";

pauseBn.style.marginLeft="10px";

pauseBn.onclick=function(){

$s.SnakeContext.pause();

};

$s.SnakeContext._pauseBn=pauseBn;

document.body.appendChild(pauseBn);

/*

varstopBn=document.createElement("button");

stopBn.innerHTML="停止";

stopBn.style.marginLeft="10px";

stopBn.onclick=function(){

$s.SnakeContext.stop();

};

$s.SnakeContext._stopBn=stopBn;

document.body.appendChild(stopBn);

*/

varrestartBn=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);

varline=document.createElement("div");

line.style.height="10px";

document.body.appendChild(line);

varspan=document.createElement("span");

span.style.color="gray";

span.style.fontSize="12px";

span.innerHTML="调试";

document.body.appendChild(span);

vardebug=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(varfinthis.foods){

if(f.x==x&&f.y==y){

returnf;

}

}

returnnull;

},

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){

varcode=null;

if(window.event){//fuck的IE

code=window.event.keyCode;

}else{

code=e.keyCode;

}

varstr="";

varoldDir=$s.SnakeContext.dir;

switch(code){

case37://left

if($s.SnakeContext.dir!=$s.Dir.RIGHT){

$s.SnakeContext.dir=$s.Dir.LEFT;

}

str="left";

break;

case38://up

if($s.SnakeContext.dir!=$s.Dir.DOWN){

$s.SnakeContext.dir=$s.Dir.UP;

}

str="up";

break;

case39://right

if($s.SnakeContext.dir!=$s.Dir.LEFT){

$s.SnakeContext.dir=$s.Dir.RIGHT;

}

str="right";

break;

case40://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){

varv=$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);

}

};

varloc=$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;

}

varloc=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

varx=loc.x;

vary=loc.y;

for(vari=0;i<this.snake.units.length;i++){

varu=this.snake.units[i];

if(u.x==x&&u.y==y){

returntrue;

}

}

returnfalse;

},

hasFood:function(x,y){

if($s.DEBUG){

varxt=$s($s.HEAD_LOCATION_ID);

if(xt){

xt.innerHTML="头部位置:("+x+","+y+")";

}

}

returnthis.food.locateOn(x,y);

},

setDebug:function(enable){

if(enable!=$s.DEBUG){

$s.DEBUG=enable;

if($s.DEBUG){//显示

vari=$s($s.KEY_UP_DIR_ID);

$s.SnakeContext._show(i);

i=$s($s.HEAD_LOCATION_ID);

$s.SnakeContext._show(i);

}else{//隐藏

vari=$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

};

})();

计算机二手书经典

http://pcenshao.taobao.com

相关推荐