先放一张成品图
首先在写贪吃蛇之前,理一下思路。
- 选择什么结构存储贪吃蛇
- 如何进行移动
- 如何吃到食物
- 吃到食物怎么增加身体
- 怎么才能结束游戏
好了,理清这几点之后,我们就可以着手进行游戏结构的编写啦
٩(๑> ₃ <)۶з。
首先我们采用二维数组来存储贪吃蛇,用坐标来初始化蛇头以及蛇身。
(蛇的每一节都是20*20)
var snake = function(){
this.bodyArr = [
{x: 0,y: 0},
{x: 0,y: 20},
{x: 0,y: 40},
{x: 0,y: 60},
{x: 0,y: 80},
{x: 0,y: 100}, //这是蛇头
];
this.dir = 'DOWN'; //默认方向向下运动
}
其实完成蛇的初始化就已经相当于完成了一半啦,剩下的就是慢慢添加方法函数了。
我们将所有的方法都选择添加在原型链上。
首先将蛇渲染到页面上,这个就不多赘述了。然后就是怎么让蛇跑起来的问题了,这里我们只需要控制蛇头的移动就行了,蛇身就依次往前挪动一格,然后删除以前的蛇再重绘即可。
然后添加键盘事件,选择上下左右进行移动,这里移动有一个简单的小逻辑。
比如我们按 ‘左’ 时,蛇正在向右或者向左移动时,都不能进行操作。其余方向同理。
那怎么吃到食物呢,首先设置随机数让食物随机刷新,蛇头吃到食物,删除当前食物再重绘。
在判断如何吃到食物时,很简单,只需要判断蛇头和食物的横纵距离是否相等即可。那么该如何添加蛇身进去呢,这是个问题,不是简单的添加一个元素在蛇的尾部,因为蛇有四个运动的方向,需要判断当前运动的方向,再添加对应的坐标。
if (arr[0].x === arr[1].x) { //纵向移动
x = arr[0].x;
if (arr[0].y > arr[1].y) { //向上移动
y = arr[0].y + 20;
} else if (arr[0].y < arr[1].y) { // 向下移动
y = arr[0].y - 20;
}
} else if (arr[0].y === arr[1].y) { // 横向移动
y = arr[0].y;
if (arr[0].x > arr[1].x) { //向左移动
x = arr[0].x + 20;
} else if (arr[0].x < arr[0].x) { //向右移动
x = arr[0].x - 20;
}
}
arr.unshift({ x, y })
完成至此,已经把贪吃蛇的基本功能都实现了,现在只是稍微丰富下边边角角即可畅玩游戏啦(๑¯ิε ¯ิ๑)
我在设置蛇头移动时,没有设置触碰边界就死亡,而是从哪个方向出去,就从相反的方向进来,只有触碰到自己的身体时,才会死亡。实现方式多种多样,看个人喜好。
剩下的得分就很简单了,只需要在吃到食物时,分数累加即可。
是不是感觉贪吃蛇也并没有那么难呢。
原生js实现代码略多,可以用canvas重写下。不过逻辑都是一样的哦…
以下附上完整代码:index.html
<div class="wrap">
<div class="score">
<span>得分:</span>
<span class="result"></span>
</div>
</div>
index.css
<style>
.wrap {
width: 500px;
height: 500px;
margin: 100px auto;
background-color: #000;
position: relative;
overflow: hidden;
}
.round {
display: block;
position: absolute;
width: 20px;
height: 20px;
background-color: green;
border-radius: 50%;
}
.round.head {
background-color: red;
}
.food {
display: block;
position: absolute;
width: 20px;
height: 20px;
background-color: purple;
border-radius: 50%;
}
.wrap .score {
color: yellow;
position: absolute;
top: 30px;
left: 40%;
z-index: 999;
}
</style>
index.js
window.onload = function() {
init();
}
function init() {
initGame();
}
var initGame = (function() {
var wrap = document.getElementsByClassName('wrap')[0],
result = document.getElementsByClassName('result')[0];
wrapW = wrap.offsetWidth,
wrapH = wrap.offsetHeight,
count = 0;
t = null;
var snake = function() {
this.bodyArr = [
{ x: 0, y: 0 },
{ x: 0, y: 20 },
{ x: 0, y: 40 },
{ x: 0, y: 60 },
{ x: 0, y: 80 },
{ x: 0, y: 100 },
];
this.dir = 'DOWN';
}
snake.prototype = {
init: function() {
this.initsnake();
this.run();
this.bindEvent();
this.food();
},
bindEvent: function() {
var _self = this;
document.addEventListener('keydown', function() {
_self.changeDir();
}, false);
},
initsnake: function() {
var arr = this.bodyArr,
len = arr.length,
frag = document.createDocumentFragment(),
item;
for (var i = 0; i < len; i++) {
item = arr[i];
var round = document.createElement('i');
round.className = i === len - 1 ? "round head" : "round";
round.style.left = item.x + "px";
round.style.top = item.y + 'px';
frag.appendChild(round);
}
wrap.appendChild(frag);
},
run: function() {
var _self = this;
t = setInterval(function() {
_self.move();
}, 500);
},
move: function() {
var arr = this.bodyArr,
len = arr.length;
for (var i = 0; i < len; i++) {
if (i === len - 1) {
this.setHeadXY(arr);
} else {
arr[i].x = arr[i + 1].x;
arr[i].y = arr[i + 1].y;
}
}
this.removeSnake();
this.initsnake();
this.dead(arr);
this.eatFood(arr);
},
dead: function(arr) {
var headX = arr[arr.length - 1].x,
headY = arr[arr.length - 1].y,
item;
for (var i = 0; i < arr.length - 2; i++) {
item = arr[i];
if (headX === item.x && headY === item.y) {
var _self = this;
setTimeout(function() {
alert('over');
clearInterval(t);
_self.removeSnake();
}, 200)
}
}
},
setHeadXY: function(arr) {
var head = arr[arr.length - 1];
switch (this.dir) {
case 'LEFT':
if (head.x <= 0) {
head.x = wrapW - 20;
} else {
head.x -= 20;
}
break;
case 'RIGHT':
if (head.x >= wrapW - 20) {
head.x = 0;
} else {
head.x += 20;
}
break;
case 'UP':
if (head.y <= 0) {
head.y = wrapH - 20;
} else {
head.y -= 20;
}
break;
case 'DOWN':
if (head.y >= wrapH - 20) {
head.y = 0;
} else {
head.y += 20;
}
break;
default:
break;
}
},
removeSnake: function() {
var bodys = document.getElementsByClassName('round');
while (bodys.length > 0) {
bodys[0].remove();
}
},
changeDir: function(e) {
var e = e || window.event,
code = e.keyCode;
this.setDir(code);
},
setDir: function(code) {
switch (code) {
case 37:
if (this.dir !== 'RIGHT' && this.dir !== 'LEFT') {
this.dir = "LEFT";
}
break;
case 38:
if (this.dir !== 'UP' && this.dir !== 'DOWN') {
this.dir = "UP";
}
break;
case 39:
if (this.dir !== 'RIGHT' && this.dir !== 'LEFT') {
this.dir = "RIGHT";
}
break;
case 40:
if (this.dir !== 'UP' && this.dir !== 'DOWN') {
this.dir = "DOWN";
}
break;
default:
break;
}
},
food: function() {
var food = document.createElement('i');
food.className = 'food';
food.style.left = this.foodPos(wrapW) * 20 + 'px';
food.style.top = this.foodPos(wrapH) * 20 + 'px';
wrap.appendChild(food);
},
foodPos: function(WorH) {
return Math.floor(Math.random() * (WorH / 20));
},
eatFood: function(arr) {
var food = document.getElementsByClassName('food')[0],
foodX = food.offsetLeft,
foodY = food.offsetTop,
headX = arr[arr.length - 1].x,
headY = arr[arr.length - 1].y,
x,
y;
if (headX === foodX && headY === foodY) {
this.removeFood();
this.score();
this.food();
if (arr[0].x === arr[1].x) {
x = arr[0].x;
if (arr[0].y > arr[1].y) {
y = arr[0].y + 20;
} else if (arr[0].y < arr[1].y) {
y = arr[0].y - 20;
}
} else if (arr[0].y === arr[1].y) {
y = arr[0].y;
if (arr[0].x > arr[1].x) {
x = arr[0].x + 20;
} else if (arr[0].x < arr[0].x) {
x = arr[0].x - 20;
}
}
arr.unshift({ x, y })
}
},
removeFood: function() {
var food = document.getElementsByClassName('food')[0];
food.remove();
},
score: function() {
result.innerText = ++count;
}
}
return new snake().init();
});