具体流程,就是先准备好要吃的食物,和蛇的构造(简单就好了。),以及游戏棋盘(我又不做 3D 的。)
初始元素设置
代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<style>
table{border:1px solid #000;background:#000;}
table tr>td{width:30px;height:30px;}
table tr td{border:1px solid #777;box-sizing:border-box;}
.food{width:30px;height:30px;border-radius:15px;background:#666;font-size:10px;line-height:30px;text-align:center;box-sizing:border-box;}
.strawberry{background:pink;color:green;border:1px solid #faa;}
.banana{background:yellow;color:#fa0;border:1px solid #fe0;}
.grape{background:purple;color:#3ae;border:1px solid #a0a;}
.div{height:30px;background:#000;padding-left:20px;}
.div>div{float:left;width:30px;height:30px;}
.head{background:#fff;opacity:0.8;}
.body{background:#fff;opacity:0.5;}
.tail{background:#fff;opacity:0.2;}
</style>
</head>
<body>
<ul>
<li>
<div class="food strawberry">草莓</div>
<div class="food banana">香蕉</div>
<div class="food grape">葡萄</div>
<p>这是食物</p>
</li>
<li>
<div class="div">
<div class="head"></div>
<div class="body"></div>
<div class="body"></div>
<div class="tail"></div>
</div>
<p>这是一条贪吃的幽灵蛇</p>
</li>
</ul>
<table id="Checkerboard"> <!-- 棋盘 -->
</table>
</body>
</html>
<script>
function Checkerboard(obj = null){
var $this = this;
if(obj === null){
obj = document.getElementById('Checkerboard');
}
$this.obj = obj;
$this.xNum = 15; // 定义 横 向数量
$this.yNum = 15; // 定义 纵 向数量
$this.run = function(){ // 运行
var html = '<tbody>';
for(var i = 0;i < $this.yNum;i++){
html += '<tr>';
for(var ii = 0;ii < $this.xNum;ii++){
html += '<td>';
}
html += '</tr>';
}
html += '</tbody>';
$this.obj.innerHTML = html; // 写入 html
}
}
var obj = document.getElementById('Checkerboard'); // 对象目标
var board = new Checkerboard(obj);
board.run(); // 运行动作
</script>
游戏开始准备
接下来就是要把你的这条蛇初始放入游戏棋盘中了,这里将不设置开始键,等后续按钮设置好后,直接点击按钮直接开始游戏。
代码:
// 算了,我懒了,所以一次性将代码放上来,然后再详细解说吧。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<style>
table{border:1px solid #000;background:#000;}
table tr>td{width:30px;height:30px;}
table tr td{border:1px solid #777;box-sizing:border-box;}
.food{width:30px;height:30px;border-radius:15px;background:#666;font-size:10px;line-height:30px;text-align:center;box-sizing:border-box;}
.strawberry{background:#fcc;color:green;border:1px solid #faa;}
.banana{background:#fe8;color:#fa0;border:1px solid #fe0;}
.grape{background:#828;color:#3ae;border:1px solid #a0a;}
.div{height:30px;background:#000;padding-left:20px;}
.div>div{float:left;width:30px;height:30px;}
.head{background:#fff;opacity:0.6;text-align:center;line-height:30px;}
.body{background:#fff;opacity:0.3;}
.tail{background:#fff;opacity:0.2;}
.str{background:#fcc;}
.ban{background:#fe8;}
.gra{background:#828;}
</style>
</head>
<body>
<ul>
<li>
<div class="food strawberry">草莓</div>
<div class="food banana">香蕉</div>
<div class="food grape">葡萄</div>
<p>这是食物</p>
</li>
<li>
<div class="div">
<div class="se head">蛇</div>
<div class="se body"></div>
<div class="se body"></div>
<div class="se tail"></div>
</div>
<p>这是一条贪吃的幽灵蛇</p>
</li>
</ul>
<table id="Checkerboard"> <!-- 棋盘 -->
</table>
</body>
</html>
<script>
function Checkerboard(obj = null){
var $this = this;
if(obj === null){
obj = document.getElementById('Checkerboard');
}
$this.obj = obj;
$this.xNum = 20; // 定义 横 向数量
$this.yNum = 20; // 定义 纵 向数量
$this.food = [['strawberry','草莓','str'],['banana','香蕉','ban'],['grape','葡萄','gra']]; // 食物类名
$this.type = 2; // 游戏模式,1 可以穿墙 2 不可以穿墙
/**
按钮定义
**/
var dict = {'a':65,'b':66,'c':67,'d':68,'e':69,"f":70,"g":71,"h":72,"i":73,"j":74,"k":75,"l":76,"m":77,"n":78,"o":79,"p":80,"q":81,"r":82,"s":83,"t":84,"u":85,"v":86,"w":87,"x":88,"y":89,"z":90,"up":38,"right":39,"left":37,"down":40};
$this.up = 'up';
$this.left = 'left';
$this.right = 'right';
$this.down = 'down';
/**
速度控制
**/
$this.maxSpeed = 10; // 最快速度,最快不得低于每 10 毫秒移动一次
maxSpeed = 10;
$this.minSpeed = 160; // 最慢速度不得高于每1秒移动一次
minSpeed = 1000;
state = true; // 游戏状态 false 为结束
valueOpition = [[0,0,'fff'],[0,1,'fff'],[0,2,'fff'],[0,3,'fff']]; // 初始化坐标
var map = new Array, // 地图
newMap = null, // 标识使用
moveD = null, // 蛇的移动方向
moveED = null, // 蛇的尾部方向,用于生成新的身板。
foodOption, // 食物位置
objLength, // 蛇的长度
objOpsition = new Array; // 蛇的位置信息
$this.run = function(){ // 运行
var html = '<tbody>';
for(var i = 0;i < $this.yNum;i++){
html += '<tr>';
for(var ii = 0;ii < $this.xNum;ii++){
html += '<td>';
}
html += '</tr>';
}
html += '</tbody>';
$this.obj.innerHTML = html; // 写入 html
// 因为坐标将通过索引位置来进行查找,所以在这之前需要为每个元素添加对应的索引。
// 索引初始化
var tbody = $this.obj.childNodes;
var tr = tbody[0].childNodes;
for(var i = 0;i < tr.length;i++){
var td = tr[i].childNodes;
tr[i].index = i;
for(var ii = 0;ii < td.length;ii++){
td[ii].index = ii;
map.push([ii,i]);
}
}
// 数据初始化
state = true;
objLength = valueOpition.length;
if($this.maxSpeed < maxSpeed)$this.maxSpeed = maxSpeed;
if($this.minSpeed > minSpeed)$this.minSpeed = minSpeed;
objOpsition = valueOpition;
createObj(valueOpition); // 生成蛇初始位置
createFood(tr); // 生成食物
controller(); // 控制器
move();
}
/**
这是对蛇的操作部分
**/
// 按钮控制,让蛇动起来。
// 修改蛇头移动方向,让下次运动的时候朝着对应方向前进
var controller = function(){
document.onkeydown = function(event){
event = event || window.event;
var code = event.keyCode;
// 上 up
var upCode = $this.up.toLowerCase();
upCode = dict[upCode];
// 下 down
var downCode = $this.down.toLowerCase();
downCode = dict[downCode];
// 左 right
var rightCode = $this.right.toLowerCase();
rightCode = dict[rightCode];
// 右 left
var leftCode = $this.left.toLowerCase();
leftCode = dict[leftCode];
switch(true){
case (code == upCode):
if(moveD != 'down'){
moveD = 'up';
}
break;
case (code == downCode):
if(moveD != 'up'){
moveD = 'down';
}
break;
case (code == rightCode):
if(moveD != 'left'){
moveD = 'right';
}
break;
case code == leftCode:
if(moveD != 'right'){
moveD = 'left';
}
break;
default:
}
}
}
var move = function(){ // 蛇的移动
num = 10; // 帧频时间设置
// 速度计算
var time = $this.minSpeed / (objLength - valueOpition.length + 1);
if(time < $this.maxSpeed){
time = $this.maxSpeed;
}
var timer = 0;
// 运动执行
var interval = setInterval(function(){
if(moveD != null && timer > time){ // 有方向,证明游戏开始了。
timer = 0; // 重置计时器
// 清空蛇身
var obj = $this.obj.getElementsByClassName('se');
var len = obj.length
for(var i = 0;i < len;i++){
obj[0].innerHTML = '';
obj[0].classList = new Array;
}
// 重新生成
objOpsition.pop();
var x = objOpsition[0][0];
var y = objOpsition[0][1];
switch(moveD){
case 'up':
y--;
break;
case 'down':
y++;
break;
case 'right':
x++;
break;
case 'left':
x--;
break;
}
// 游戏模式检测
switch($this.type){
case 1:
if(x < 0 || x >= $this.xNum || y < 0 || y >= $this.yNum){
state = false;
}
break;
case 2:
if(x < 0){
x = $this.xNum - 1;
}else if(x >= $this.xNum){
x = 0;
}
if(y < 0){
y = $this.yNum - 1;
}else if(y >= $this.yNum){
y = 0;
}
break;
}
if(state){
// 检测是否吃东西了没?
tertingTakeFood(x,y);
objOpsition.unshift([x,y,'fff']);
var result = createObj(objOpsition); // 创建蛇
if(!result){
over(interval);
}
}else{
over(interval);
}
}
timer += num; // 时间叠加
},num);
}
// 根据传入数据,解析坐标,然后生成对应的蛇~~~
var createObj = function(optionArr){ // 在棋盘中创建蛇
objOpsition = new Array; // 重新编写位置信息。
var tr = $this.obj.childNodes[0].childNodes;
var jsonStr = JSON.stringify(optionArr);
for(var i = 0;i < optionArr.length;i++){
var x = optionArr[i][0];
var y = optionArr[i][1];
if(i >= optionArr.length - 1){
var color = optionArr[i][2];
}else{
var color = optionArr[i + 1][2];
}
switch(true){
case i == 0:
// 蛇头 head
// 检测是否咬到自身
var str = JSON.stringify(optionArr[i]);
var lstr = jsonStr.slice(jsonStr.lastIndexOf(str) + str.length);
str = str.slice(0,str.lastIndexOf(',')) + ',';
if( lstr.indexOf(str) != -1){
return false; // 咬到自己了,返回失败
}
tr[y].childNodes[x].classList.add('se','head',color);
tr[y].childNodes[x].innerHTML = '蛇';
break;
case i > 0 && (i < optionArr.length - 1):
// 蛇身 body
tr[y].childNodes[x].classList.add('se','body',color);
break;
default:
// 记录尾部方向,相对于前面那个,尾部在它的哪个方向
var lx = optionArr[(i - 1)][0];
var ly = optionArr[(i - 1)][1];
switch(true){
case lx > x:
moveED = 'left';
break;
case lx < x:
moveED = 'right';
break;
case ly > y:
moveED = 'up';
break;
case ly < y:
moveED = 'down';
break;
}
// 蛇尾 tail
tr[y].childNodes[x].classList.add('se','tail',color);
}
objOpsition.push([x,y,color]);
}
return true;
}
// 检测吃东西了没
var tertingTakeFood = function(x,y){
if(x == foodOption[0] && y == foodOption[1]){
var len = objOpsition.length;
var l = objOpsition[(len - 1)]; // 临时数据
x = l[0];
y = l[1];
var color = l[2];
objOpsition[(len - 1)][2] = foodOption[2];
switch(moveED){
case 'up':
y--;
break;
case 'down':
y++;
break;
case 'left':
x--;
break;
case 'right':
x++;
break;
}
objOpsition.push([x,y,color]);
$this.obj.getElementsByClassName('food')[0].innerHTML = '';
$this.obj.getElementsByClassName('food')[0].classList = new Array();
createFood(); // 重新生成食物
}
}
/**
这是对食物的操作部分
**/
var createFood = function(tr = null){ // 食物生成
if(tr === null){
tr = $this.obj.childNodes[0].childNodes;
}
var index = rand($this.food.length,0); // 随机获取一个食物
var food = $this.food[index];
var CFoodOpsition = function(){ // 生成食物位置
if(newMap === null){
newMap = createNewMap();
}
var x = rand($this.xNum,0);
var y = rand($this.yNum,0);
if(newMap.indexOf('[' + x + ',' + y + ']') != -1){
newMap = null; // 更新标识
return [x,y];
}else{
return CFoodOpsition();
}
}
var opsition = CFoodOpsition();
var x = opsition[0];
var y = opsition[1];
var obj = tr[y].childNodes[x];
obj.classList.add('food',food[0]);
obj.innerHTML = food[1];
foodOption = [x,y,food[2]]; // 记录食物所在位置
}
var createNewMap = function(){ // 新的可用地图
// 将地图 map 转换成 json 字符串,然后处理结束后,再进行转换。
var jsonStr = JSON.stringify(map); // json 字符串
var returnMap;
for(var i = 0;i < objOpsition.length;i++){
var x = objOpsition[i][0];
var y = objOpsition[i][1];
opsitionStr = '[' + x + ',' + y + ']';
var index = jsonStr.indexOf(opsitionStr);
var p = jsonStr.slice(0,index);
var n = jsonStr.slice((index + opsitionStr.length)).replace(/^\,s*/g,"");
jsonStr = p + n;
}
returnMap = jsonStr;
return returnMap;
};
var rand = function(max = 10,min = 0){ // 随机数
var num = 0;
if(max > min){
num = Math.floor(Math.random() * (max - min)) + min;
}
return num;
};
var over = function(interval){
alert('GAME OVER!');
$this.run(); // 重新开始
clearInterval(interval);
moveD = null;
}
}
var obj = document.getElementById('Checkerboard'); // 对象目标
var board = new Checkerboard(obj);
board.run(); // 运行动作
</script>
// 整个贪吃蛇逻辑,分为三个部分:
一、游戏初始设置。
二、蛇的运动控制
蛇的运动,一需要不断的渲染,以及蛇的移动方向变更,最后的就是蛇的进食
不断渲染,这样才能让蛇成为“移动的”,需要设置一个创建蛇身体的函数,根据传入的蛇位置信息,进行创建一个新的蛇。并且,设立一个不断在运行的方法,对蛇不断地更新,需要不断地删除它,然后依据新的位置重新生成,来达成移动的效果。并且如果你的设定是蛇不能咬自己的,还需要在生成蛇的时候进行判断,自己的头是否和身体的某个部分进行重合了。
移动方向的控制:这里移动方向的控制,就需要根据设定好了的四个按钮,触发事件然后根据不同按钮,修改属性 moveD 来修改蛇移动的方向,最终让在不断渲染的方法,依据该属性,不断地修改蛇的位置信息,最后再生成新的蛇,这样看起来就是在不断前进了。
蛇的进食:蛇的进食和是否咬身体的逻辑一样,检测头是否和食物进行重合,这里我的食物是用类名 food 作为标记的。在进食食物后,需要注意的一点是,要重新生成新的食物。
三、食物的控制
食物的控制,其实也就只是食物的生成而已,需要注意的是,生成的食物,是不能和蛇的身体进行重合的,而且食物的位置是随机的,需要进行随机,并且判断是否在允许位置才能生成。