JS实现贪吃蛇游戏

设计

规则

  1. 贪吃蛇初始为 1 点,可通过上、下、左、右键控制方向移动
  2. 每吃一个食物分数+1,每吃 10 个食物,等级+1,每升一级,速度 + 20ms
  3. 贪吃虫不能撞墙和自己,否则游戏失败
  4. 每吃掉一个食物,贪吃蛇长度+1,下一个食物会在随机位置出现。

实现

HTML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div id="main">
<div id="stage">
<div id="snake">
<div></div>
</div>
<div id="food">
<div class=""></div>
<div class=""></div>
<div class=""></div>
<div class=""></div>
</div>
</div>
<div id="info">
<div class="">SCORE: <span id="score">0</span></div>
<div class="">LEVEL: <span id="level">1</span></div>
</div>
</div>

JS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
const snake = document.getElementById("snake");
const snakes = snake.getElementsByTagName("div");
const food = document.querySelector("#food");
const scoreElement = document.getElementById("score");
const levelElement = document.getElementById("level");

let dir,
keyActive = true,
gaveOver = false,
level = 1,
score = 0,
speed = 300;

const keyArr = ["ArrowUp", "ArrowDown", "ArrowRight", "ArrowLeft"];

const dirObj = {
ArrowUp: "ArrowDown",
ArrowDown: "ArrowUp",
ArrowRight: "ArrowLeft",
ArrowLeft: "ArrowRight",
};

// 监听键盘事件, 保存方向
document.addEventListener("keydown", (e) => {
if (
keyActive &&
//只监听上下左右方向
keyArr.includes(e.key) &&
//不能反方向,除非蛇的长度等于1
(snakes.length < 2 || dirObj[dir] !== e.key)
) {
dir = e.key;
keyActive = false;
}
});

function changeFood() {
// 获取0-29之间的随机数,然后*10,即0-290
const x = Math.floor(Math.random() * 30) * 10;
const y = Math.floor(Math.random() * 30) * 10;
food.style.top = y + "px";
food.style.left = x + "px";
}

setTimeout(function move() {
const head = snakes[0];
let y = head.offsetTop,
x = head.offsetLeft;
switch (dir) {
case "ArrowUp":
y -= 10;
break;
case "ArrowDown":
y += 10;
break;
case "ArrowRight":
x += 10;
break;
case "ArrowLeft":
x -= 10;
break;
}
//如果食物坐标和头部坐标相吻合,说明蛇吃到了食物,然后改变食物的坐标到新的随机坐标,并把蛇长度+1,分数+1,如果此时满10个了,等级再+1,速度提升20ms
if (
food.offsetTop === head.offsetTop &&
food.offsetLeft === head.offsetLeft
) {
changeFood();
snake.insertAdjacentHTML("beforeend", "<div/>");
score++;
scoreElement.textContent = score;
if (score % 10 === 0 && level < 14) {
level++;
levelElement.textContent = level;
speed = speed - level * 20;
}
}

//撞墙或撞自己就死了
if (x < 0 || x > 290 || y < 0 || y > 290) {
alert(`撞墙了,游戏结束!\n您的分数为${score}分`);
return;
}

if (snakes.length > 1) {
for (let i = 0; i < snakes.length - 1; i++) {
if (snakes[i].offsetTop === y && snakes[i].offsetLeft === x) {
alert(`撞自己了,游戏结束!\n您的分数为${score}分`);
return;
}
}
}
//蛇的移动:蛇的尾部挪到蛇的头部,就可以实现代价最小的移动,
const tail = snakes[snakes.length - 1];
tail.style.left = x + "px";
tail.style.top = y + "px";
snake.insertAdjacentElement("afterbegin", tail);
keyActive = true;

setTimeout(move, speed);
}, speed);

CSS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
#main {
width: 360px;
height: 420px;
border: 10px solid #000;
background-color: #b7d4a8;
border-radius: 20px;
margin: 50px auto;
}
#stage {
width: 304px;
height: 304px;
border: 2px solid #000;
margin: 20px auto;
position: relative;
}
#snake > div {
width: 10px;
height: 10px;
background-color: #000;
position: absolute;
border: 1px solid #b7d4a8;
}
#food {
width: 10px;
height: 10px;
position: absolute;
display: flex;
flex-wrap: wrap;
top: 100px;
left: 120px;
}
#food > div {
width: 5px;
height: 5px;
background-color: #000;
transform: rotate(45deg);
}
#info {
width: 304px;
margin: 0 auto;
display: flex;
justify-content: space-between;
font: bold 20px courier;
}