<style>
input{
border:1px solid #000;
background:#fff;
padding:5px ;
height:20px;
margin:5px;
font:12px/1 "";
cursor: pointer;
}
table{
/* background:#ccc; */
border: 1px solid #000;
border-collapse:collapse;
margin:30px auto;
}
tr,td{
border: 1px solid #000;
border-collapse:collapse;
}
td{
width: 18px;
height: 18px;
}
.red{
background-color: red;
}
.tou{
background:url(images/shetou.png) no-repeat;
background-size:18px 18px;
}
.shen{
background:url(images/sheshen.png) no-repeat;
background-size:18px 18px;
}
.wei{
background:url(images/shewei.png) no-repeat;
background-size:18px 18px;
}
p{
margin:50px auto;
font:700 40px/1 "";
color:#000;
text-align:center;
}
</style>
下面是Html的代码
<input type="button" value="开始" id="start">
<input type="button" value="暂停" id="stop">
<input type="button" value="重新开始" id="reset"><br>
<input type="button" value="简单" id="easy" class="red">
<input type="button" value="中等" id="middle">
<input type="button" value="困难" id="hard">
<table></table>
<p>5</p>
接下来是原生JS部分
//一共分了三个类,一个是地图类即小蛇运动的范围,一个是小蛇,还有一个是小蛇的食物。
var op=document.getElementsByTagName("p")[0];
var ostart=document.getElementById("start");
var ostop=document.getElementById("stop");
var oreset=document.getElementById("reset");
var oeasy=document.getElementById("easy");
var omiddle=document.getElementById("middle");
var ohard=document.getElementById("hard");//这里获取一些按钮的节点以及显示分数的p标签节点
var lock=false;//防止按钮被一直点击出现bug
var interval=400;
function Map(row,col){ //构造一个Map类,它能够实现控制背景小方格的行数及列数
this.row=row; //接收传入的参数
this.col=col;
this.creat(); //调用creat方法让它在屏幕上显示出来
}
Map.prototype.creat=function(){
this.otable=document.getElementsByTagName("table")[0]; //获取表格的节点
this.otds=[];//创造一个表格二维数组来接收每一个方格,每一项即为一行,而每一行中又包含这一行中的所有列的小方格节点
for(var i=0;i<this.row;i++){
this.otr=document.createElement("tr");//根据参数row来创造行tr的数量
this.otrtds=[]; //创造这一行的空数组来接收这一行中所有列的节点
for(var j=0;j<this.col;j++){
this.otd=document.createElement("td");//创建每行中的所有列td节点
this.otr.appendChild(this.otd);//并把列td节点追加到这一行中
this.otrtds.push(this.otd);//同时把列td节点添加这一行的数组中
}
this.otable.appendChild(this.otr);//把这一行的节点追加到表格table节点中
this.otds.push(this.otrtds);//同时将这一行添加到之前这个表格的数组中
}
}// 总的来说creat方法能实现的功能就是将所有的小方格的节点都存放map.otds[][]这个二维数组中,这样我们就可以精确控制每一个小方格了
//比如我们想要控制第3行第2列的小方格,map.otds[2][1].className="red"即可
function Snake(){ //创造一个小蛇类
this.body=[ //设置小蛇的初始位置,小蛇的头是第一项
{r:2,c:4},
{r:2,c:3},
{r:2,c:2},
{r:2,c:1},
{r:2,c:0}
];
this.direction = "right"; //设置小蛇的初始运动方向
this.render();
this.bindEvent();
}
Snake.prototype.render=function(){ //设置小蛇的渲染方法即根据小蛇的body属性来在地图上显示出小蛇
for(var j=0;j<map.row;j++){ //显示之前先把地图上所有点的颜色都清除
for(var k=0;k<map.col;k++){
map.otds[j][k].className="";
map.otds[j][k].innerHTML="";
}
}
map.otds[this.body[0].r][this.body[0].c].innerHTML=this.body.length;
map.otds[this.body[0].r][this.body[0].c].className="tou";
for(var i=1;i<this.body.length-1;i++){//然后显示小蛇所在位置表格的颜色
map.otds[this.body[i].r][this.body[i].c].className="shen";
}
map.otds[this.body[this.body.length-1].r][this.body[this.body.length-1].c].className="wei";
}
Snake.prototype.move=function(){//设置小蛇的运动方法,小蛇的运动原理即是每次在小蛇的body中根据运动方向添加一项在头部,同时把最后一项删掉
this.body.pop();
// console.log(this.body);
switch(this.direction){
case "right":
this.body.unshift({r:this.body[0].r,c:this.body[0].c+1});//比如向右运动的时候在body的第一项前面添加一项表示在它右边一格位置的对象即{r不变,c+1}的数组
break;
case "left":
this.body.unshift({r:this.body[0].r,c:this.body[0].c-1});
break;
case "down":
this.body.unshift({r:this.body[0].r+1,c:this.body[0].c});
break;
case "top":
this.body.unshift({r:this.body[0].r-1,c:this.body[0].c});
break;
}
if(food.r==this.body[0].r&&food.c==this.body[0].c){//当小蛇的头部碰到了食物的时候
this.growup(); //让小蛇的长度增加一次
food.produce(); //同时产生新的食物的位置 。因为小蛇的move方法是在new Food()后面运行的,所以这里可以这样写,如果把move方法添加到Snake()构造函数中的话程序则会报错,因为你是先new的Snake的这时候还不知道food.produce是什么东西。
}
if(this.body[0].r<0||this.body[0].r>map.row-1||this.body[0].c<0||this.body[0].c>map.col-1){//当小蛇的头部走出了表格的范围时候,游戏结束
clearInterval(t);
alert("game over");
}
for(var i=1;i<this.body.length;i++){
if(this.body[0].r==this.body[i].r&&this.body[0].c==this.body[i].c){//用for循环的方式来判断小蛇的头部是否碰到自己的身体,如果有则游戏结束
clearInterval(t);
alert("game over");
}
}
op.innerHTML=this.body.length;//同时将小蛇的长度更新在p标签里显示
}
Snake.prototype.growup=function(){//这里是小蛇增长的原理,即在移动的原理上少了一个减去最后一项
switch(this.direction){
case "right":
this.body.unshift({r:this.body[0].r,c:this.body[0].c+1});
break;
case "left":
this.body.unshift({r:this.body[0].r,c:this.body[0].c-1});
break;
case "top":
this.body.unshift({r:this.body[0].r-1,c:this.body[0].c});
break;
case "down":
this.body.unshift({r:this.body[0].r+1,c:this.body[0].c});
break;
}
}
Snake.prototype.bindEvent=function(){//给小蛇添加键盘控制事件
var self=this;
document.onkeydown=function(event){
var e=event||window.event;
var keycode=e.keyCode||e.which;
// console.log(keycode)
switch(keycode){
case 37://当按下的键为左键时,先要判断当前小蛇的运动方向是否为往右,如果是则直接return表示按下无效,如果不是则改变方向为左
if(self.direction=="right"){return;}
self.direction="left";
break;
case 38://同理
if(self.direction=="down"){return;}
self.direction="top";
console.log(self.direction);
break;
case 39:
if(self.direction=="left"){return;}
self.direction="right";
break;
case 40:
// clearInterval(t);
if(self.direction=="top"){return;}
self.direction="down";
console.log(self.direction);
break;
}
}
}
function Food(){ //构造一个Food类的函数
this.produce();
this.change();
}
Food.prototype.produce=function(){ //在table中产生一个随机位置的food
this.r=parseInt(Math.random()*map.row);
this.c=parseInt(Math.random()*map.col);
for(var i=0;i<snake.body.length;i++){ //不过产生的时候要判断一下如果这个食物刚好在小蛇身上则要重新产生一个即让这个方法重新运行一遍
if(this.r==snake.body[i].r&&this.c==snake.body[i].c){
this.produce();
return;
}
}
}
Food.prototype.change=function(){//让produce产生的小方格位置变为红色
map.otds[this.r][this.c].className="red";
}
var map=new Map(20,60);
var snake=new Snake();
var food=new Food();
oeasy.onclick=function(){
if(lock){return;}
omiddle.className="";
ohard.className="";
oeasy.className="red";
interval=400;
}
omiddle.onclick=function(){
if(lock){return;}
oeasy.className="";
ohard.className="";
omiddle.className="red";
interval=200;
}
ohard.onclick=function(){
if(lock){return;}
omiddle.className="";
oeasy.className="";
ohard.className="red";
interval=100;
}
ostart.onclick=function(){
if(lock){return;}
t=setInterval(function(){
snake.move(); //这里设置一个定时器,每200毫秒先让小球运动一次更新body的位置,接着利用render将它的位置渲染出来,因为render的时候会把表格中所有的小方格颜色清空,所以在render后面还要让food的位置显示出来
snake.render();
food.change();
},interval)
lock=true;
}
ostop.onclick=function(){
if(!lock){return;}
clearInterval(t);
lock=false;
}
oreset.onclick=function(){ //恢复初始化
clearInterval(t);
omiddle.className="";
ohard.className="";
oeasy.className="red";
interval=400;
snake.body=[
{r:2,c:4},
{r:2,c:3},
{r:2,c:2},
{r:2,c:1},
{r:2,c:0}
];
snake.direction="right";
snake.render();
food.produce();
food.change();
op.innerHTML=5;
lock=false;
}