有一天午睡突然突发奇想写个贪吃蛇玩一玩,一开始是想用c来写,然后弄一个8*8的点阵屏来玩的,但是又觉得太过于麻烦,所以就想着用最简单的方法来实现,所以就选择了JavaScript和HTML来实现,简述一下实现原理和提出代码

示例demo

snake-game
建议用手机打开,当然电脑也做了上下左右键盘控制,但是显示效果是为了手机显示适应的

效果图

效果图

实现原理

我觉得弄这个需要一点点的面相对象的思维就能简简单单的实现,首先我们需要有一个地图,所以就需要在这个地图上建立坐标系,不让我们的贪吃蛇抛出我们的坐标系,所以我就选择了二维数组来建立我们的坐标系,然后给每个坐标都设定一个值(
我设定的就是x轴左边加y轴坐标的字符串),然后我们还需要一条贪吃蛇,和砖块,这里其实可以看成一部分,其实砖块就是我们坐标里面对应的值,而我们的贪吃蛇就是这个砖块的数组,所以我们可以理解成以下样子

地图:
一个二维数组
贪吃蛇
一个包含在地图里面的一维数组 ==> 多个砖块的拼接组成的一个数组
砖块
一个地图里面对应的准确的值

所以看到这里就很简单了,所谓的贪吃蛇其实就是在操作我们熟知的数组

上述的是我们游戏里面涉及到的元素,接下来我们讲讲我们涉及到的一些动作

蛇的行动范围

这个其实很简单,主要就是对蛇头做判断(数组第一个),只要蛇头不大于地图数组,游戏便可以继续下去

蛇的位移

其实蛇移动比较容易理解,例如 向下移动的时候y轴坐标+1,x轴坐标不动 其他方向同理,这样就能实现蛇的整体位移
示例
看图比较容易看懂,就是蛇头以后得元素需要向之前的移动

蛇的成长
image.png

这里我们有使用数据结构中比较高级的插入方式,使用了我比较喜欢的渣渣方式,就是直接在数组的后面添加一个元素,实现蛇吃砖块长大的方式

砖块的随机生成
这里需要确保不会和我门蛇的数组位置冲突,需要做一些判断

蛇吃自己
这里只需要判断蛇里面没有重复的元素便可以了

视图渲染
每次蛇的移动完成以后都生成一个0和1组成的二维数组,遍历数组为HTML添加颜色就可以了

写在最后
其实实现起来远比我说明的这些要容易的多,毕竟我是一个喜欢讲废话的人,我个人觉得看了代码就知道这个东西确实是挺简单的

代码

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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="user-scalable=0">
<title>snake game</title>
<style>
* {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}

#score {
font-size: 300%;
display: flex;
align-items: center;
justify-content: center;
}

#handle {
display: flex;
flex-direction: column;
overflow: hidden;
font-size: 150%;
}

.game-row {
display: flex;
flex-direction: row;
}

.game-column {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
border: 1px #ce53d2 solid;
}

.game-column-snake {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
border: 1px #ce53d2 solid;
background: black;
}

.game-column-block {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
border: 1px #ce53d2 solid;
background: lightgreen;
}

.center {
display: flex;
align-items: center;
justify-content: center;
}
</style>
</head>
<body id="body" style="display: flex;flex-direction: column">
<div id="score">分数</div>
<div id="game"></div>
<div id="handle">
<div class="center" style="height: 30%;" onclick="handle('up')"></div>
<div style="display: flex;flex-direction: row;height: 60%;">
<div class="center" onclick="handle('left')"></div>
<div class="center" onclick="handle('start')">重新开始</div>
<div class="center" onclick="handle('right')"></div>
</div>
<div class="center" style="height: 30%;" onclick="handle('down')">

</div>
</div>
</body>
<script>
var w = 0,//页面的宽度
h = 0,//页面的高度
block = '00',//砖块
snake = [],//蛇
map = [], //实际坐标系
xy = [],//渲染坐标系
hanld = 'right', //走动方向
mapSize = 10, //地图大小
score = 0,//游戏积分
level = 500,//游戏等级,默认是800毫秒走一次
endflag = false,
runStatus = 0;//自动运行标记
main();

/**
* 主方法
*/
function main() {
initShow();
initMap();
genSnake();
genBlock();
gen01();
next();
}

/**
* 初始化页面信息
*/
function initShow() {
//获取body的高度和宽度
var dom = document.getElementById("body");
w = dom.offsetWidth, h = dom.offsetHeight;
var h_w = h - w;
//自适应分数显示区域的高度
var score = document.getElementById('score');
score.style.height = parseInt(h_w) * 0.2 + 'px';
//自适应操作示区域的高度
var handle = document.getElementById('handle');
handle.style.height = parseInt(h_w) * 0.8 + 'px';
//自适应游戏区域的高度和宽度
var game = document.getElementById('game');
game.style.width = parseInt(w) + 'px';
game.style.height = parseInt(w) + 'px';
}

/**
*初始化地图
*/
function initMap() {
for (var x = 0; x < mapSize; x++) {
map[x] = [];
for (var y = 0; y < mapSize; y++) {
map[x][y] = splitStr(y, x);
}
}
}

/**
* 生成随机数
* @param filter 需要过滤掉的数字
* @returns {number}
*/
function rand(filter) {
var rand = parseInt(Math.random() * 10);
if (rand == filter) {
rand(filter);
}
return rand;
}

/**
* 将坐标系转换成0/1的数组
*/
function gen01() {
var overflag = false;
for (var i = 0; i < mapSize; i++) {
xy[i] = [];
for (var j = 0; j < mapSize; j++) {
xy[i][j] = 0;
for (var s = 0; s < snake.length; s++) {
if (snake[s] == map[i][j]) {
xy[i][j] = 1;
break;
}
}
if (block == map[i][j]) {
xy[i][j] = 2;
}
if (snake[0] == map[i][j]) {
overflag = true;
}
}
}
if (!overflag) {
//游戏结束
showover();
} else {
if (!endflag) {
//生成进制以后就将数据渲染到HTML中去
show(xy);
}
}
}

/**
* 生成小蛇
*/
function genSnake() {
snake.push(splitStr(rand(), rand()));
}

/**
* 生成随机砖块
*/
function genBlock() {
block = splitStr(rand(), rand());
//确保生成砖块不会生成到和蛇一样的,否则就重新生成
for (var i in snake) {
if (block == snake[i]) {
genBlock();
}
}
}

/**
* 蛇向下一步移动
*/
function next() {
clearTimeout(runStatus);
changeSnakeHead();
eatself();
eatblock();
gen01();
runStatus = setTimeout(function () {
next();
}, level);
}


/**
* 判断蛇是否吃自己
*/
function eatself() {
for (var s = 0; s < snake.length; s++) {
if (s > 0 && snake[s] == snake[0]) {
showover();
break
}
}
}

/**
* 修改蛇的身体
*/
function changeSnakeHead() {
var tempa = snake[0];
for (var i = 0; i < snake.length; i++) {
var a = snake[i][0], b = snake[i][1];
if (i == 0) {
switch (hanld) {
case 'up':
snake[i] = splitStr(a, parseInt(b) - 1);
break;
case 'down':
snake[i] = splitStr(a, parseInt(b) + 1);
break;
case 'left':
snake[i] = splitStr(parseInt(a) - 1, b);
break;
case 'right':
snake[i] = splitStr(parseInt(a) + 1, b);
break;
}
} else {
var tempb = tempa;
tempa = snake[i];
snake[i] = tempb;
}
}
}

/**
* 吃砖块
*/
function eatblock() {
if (snake[0] == block) {
var tail = snake[snake.length - 1], temp = [], a = tail[0], b = tail[1];
switch (hanld) {
case 'up':
temp = splitStr(a, parseInt(b) + 1);
break;
case 'down':
temp = splitStr(a, parseInt(b) - 1);
break;
case 'left':
temp = splitStr(parseInt(a) + 1, b);
break;
case 'right':
temp = splitStr(parseInt(a) - 1, b);
break;
}
snake.push(temp);
addscore();
genBlock();
}
}

/**
* 修改蛇的身体结构
* @param x
* @param y
*/
function splitStr(x, y) {
return x + '' + y;
}


/**
* 键盘按下事件绑定
*/
document.onkeydown = function (e) {
var e = window.event || e;
switch (e.keyCode) {
case 37: //左
hanld = hanld != 'left' && hanld != 'right' ? 'left' : hanld;
break;
case 38: //上
hanld = hanld != 'up' && hanld != 'down' ? 'up' : hanld;
break;
case 39: //右
hanld = hanld != 'left' && hanld != 'right' ? 'right' : hanld;
break;
case 40: //下
hanld = hanld != 'up' && hanld != 'down' ? 'down' : hanld;
break;
}
}

/**
* 按键事件处理
*/
function handle(e) {
if (e == 'start') {
if (endflag) {
//只有输掉游戏才能重新开始游戏
window.location.reload();
}
} else {
if ((hanld == 'left' || hanld == 'right')) {
if (e == 'up' || e == 'down') {
hanld = e;
}
} else {
if (e == 'left' || e == 'right') {
hanld = e;
}
}
}
}

/**
* 添加游戏积分,目前的积分就是蛇的长度
*/
function addscore() {
var score = parseInt(snake.length) - 1;
//根据游戏得分修改游戏等级,一共分五个等级,
if (score > 20) {
level = 250;
} else if (score > 15) {
level = 300;
} else if (score > 10) {
level = 350;
} else if (score > 5) {
level = 400;
} else if (score > 1) {
level = 450;
} else {
level = level;
}
document.getElementById('score').innerHTML = '分数:' + score;
}

/**
* 将数据渲染在页面上去
* @param xy
*/
function show(xy) {
var html = '',
height = parseInt(w) / mapSize;
for (var i = 0; i < mapSize; i++) {
html += '<div class="game-row" style="height:' + height + 'px;">'
for (var j = 0; j < mapSize; j++) {
if (xy[i][j] == 1) {
html += '<div class="game-column-snake"></div>';
} else if (xy[i][j] == 2) {
html += '<div class="game-column-block"></div>';
} else {
html += '<div class="game-column"></div>';
}

}
html += '</div>';
}
document.getElementById('game').innerHTML = html;
}

/**
* 游戏结束
*/
function showover() {
document.getElementById('game').innerHTML = '<h1 align="center" style="font-size: 500%;color:red;">game over!</h1>';
endflag = true;
}


</script>
</html>