[亦疯实验室]JavaScript版贪吃蛇

共逃掉高挂科率课4节,高点名率2节,从昨天中午写到半夜,再从早八点弄到现在(等下还要抓紧时间赶物理实验报告:(),这个伟大的工程终于完成了。。。掌声+撑个懒腰。
感觉web开发语言都很神奇,几种结合起来就很容易产生不错的效果,这条蛇的起源正式来自这种想法。闲来无事,想着用JS写点好玩的东西(成品可能很弱智,至少技术上好玩。。),转瞬想到贪吃蛇,完全自己实现不太可能,毕竟毫无开发经验,果断百度,诶哟还真有,发现这么个好东西Js版贪吃蛇解析和详细注释,马上down出源码开始研究,半个下午后代码全部照写了一遍,逻辑也研究透彻了,然后到此为止??of course not!Because:

  • 做人不能太不要脸
  • 原版成品不符合亦疯一贯坚持的亮瞎狗眼原则

于是,果断开始进行改造,期间搞出过万花筒写轮蛇,二百五闪光蛇,后来实在是太过混乱,干脆从简,百度了图片两张,效果不错。
其它改造的地方包括:

  • 炸弹(贪吃蛇木有障碍肿么行,那还不横行霸道了)
  • 穿墙开关
  • 分数记录
  • 速度可自由输入
  • 速度显示

开始游戏:贪吃蛇

问题:rank应该不是最好的排序算法,我在将snake[1]加上背景色后,发现其一直在循环向后移位,但是funnyhao的注释说的是:“排列数组,时刻保持snake[1]为蛇头”,显然这里存在问题,蛇的所有结点都在变换位置,这也是导致我万花筒写轮蛇,二百五闪光蛇越写越混乱的原因。最简的做法因该可以是:蛇和食物发生碰撞时,蛇的前进状态不变,前进一步后,在蛇尾压入与原蛇尾相同的块。但是由于午饭时间到了,这样完全重构的大工程,我还是觉得午饭为重。

1
2
3
4
5
6
function rank() { // 排列数组,时刻保持snake[1]为蛇头
truefor (var len = snake.length; len > 0; len--) {
truetruesnake[len] = snake[len - 1];
true}
truesnake[0] = { };
}

本来准备做一个程序逻辑的思维导图,不过搞到现在实在是疲惫了,暂且放下。

如果您有建议或发现BUG,请回复告诉我,谢谢:)。

PS:再次感谢funnyhao。

游戏界面

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
/* 2012.05.10 by 叶科忠 */
//游戏界面参数
var widthNum = 20; // 游戏界面宽度
var heightNum = 15; // 游戏界面高度
var nodLength = 30; //单位点大小
var snake; // 蛇
var food = { }; // 食物
var obstacle; // 障碍
var speed = 0; // 速度
var bombNum = 0; // 炸弹数量
var timer; // 定时器
var next = { }; // 下一步坐标
var score = 0; // 分数
function initSnakePoint(x, y) { // 初始化蛇
truevar div = document.createElement("img");
truediv.src = "./apple_snake.png";
truediv.className = "snakePoint";
truediv.style.top = y + "px";
truediv.style.left = x + "px";
trueId("Box").appendChild(div);
truereturn div;
}
function createFood() { // 生成食物
truevar foodXY = randomFoodXY();
truevar div = document.createElement("img");
truediv.src = "./apple_snake.png";
truefood.x = foodXY[0];
truefood.y = foodXY[1];
truefood.div = div;
truediv.className = "foodPoint";
truediv.setAttribute("id", "snakePoint");
truediv.style.left = foodXY[0] * nodLength + "px";
truediv.style.top = foodXY[1] * nodLength + "px";
trueId("Box").appendChild(div);
}
function createObstacle() { // 生成障碍
truevar num = document.getElementsByName("bombNum");
truefor (var i = 0; i < num.length; i++) {
truetrueif (num[i].checked) {
truetruetruebombNum = num[i].value;
truetruetruebreak;
truetrue}
true}
truevar obstacleNum = bombNum;
truefor (var i = 0; i < obstacleNum; i++) {
truetrueobstacle[i] = {};
truetruevar obstacleXY = randomObstacleXY();
truetrueobstacle[i].x = obstacleXY[0];
truetrueobstacle[i].y = obstacleXY[1];
truetruevar div = document.createElement("img");
truetruediv.src = "./bomb.jpg";
truetrueobstacle[i].div = div;
truetruediv.className = "obstaclePoint";
truetruediv.id = "obstaclePoint";
truetruediv.style.left = obstacleXY[0] * nodLength + "px";
truetruediv.style.top = obstacleXY[1] * nodLength + "px";
truetrueId("Box").appendChild(div);
true}
}
function initGame() { // 开始游戏
trueId("start").blur();
truesnake = [];
trueobstacle = [];
truesnake[0] = {};
truefood = {};
truescore = 0;
trueId("start").disabled = true;
trueId("pause").disabled = false;
trueId("reStart").style.display = ""
truevar level = document.getElementsByName("level");
truefor (var i = 0; i < level.length; i++) {
truetrueif (level[i].checked) {
truetruetruespeed = level[i].value;
truetruetruebreak;
truetrue}
true}
trueId("showSpeed").innerHTML = speed;
snake.push(new snakePoint(randomFoodXY()[0], randomFoodXY()[1]));
truetimer = window.setInterval(run, speed);
truecreateObstacle(); // 生成障碍
truecreateFood(); // 生成食物
truelistenKey(); // 监视键盘
}
function run() {
truevar len = snake.length;
truesnake[len - 1].direction = snake[1].direction;
trueswitch (snake[1].direction) {
truetruecase 0:
truetruetruenext.y = snake[1].y - 1;
truetruetruenext.x = snake[1].x;
truetruetruejudge(0);
truetruetruebreak;
truetruecase 1:
truetruetruenext.y = snake[1].y + 1;
truetruetruenext.x = snake[1].x;
truetruetruejudge(1);
truetruetruebreak;
truetruecase 2:
truetruetruenext.y = snake[1].y;
truetruetruenext.x = snake[1].x - 1;
truetruetruejudge(2);
truetruetruebreak;
truetruecase 3:
truetruetruenext.y = snake[1].y;
truetruetruenext.x = snake[1].x + 1;
truetruetruejudge(3);
truetruetruebreak;
true}
truekey = true; // 响应键盘
}
function judge(direction) { // 判断死亡
truesnakeHead = snake[1];
truevar through;
truevar mode = document.getElementsByName("through"); // 是否穿墙
truefor (var i = 0; i < mode.length; i++) {
truetrueif (mode[i].checked) {
truetruetruethrough = mode[i].value;
truetruetruebreak;
truetrue}
true}
trueif (through == "yes") {
truetrueif (next.y == -1) {
truetruetruenext.y = 15;
truetrue} else if (next.y == 16) {
truetruetruenext.y = 0;
truetrue} else if (next.x == -1) {
truetruetruenext.x = 20;
truetrue} else if (next.x == 21) {
truetruetruenext.x = 0;
truetrue}
true} else if (through == "no") {
truetrueif (next.y == -1) {
truetruetruegameOver();
truetruetruereturn false;
truetrue} else if (next.y == 16) {
truetruetruegameOver();
truetruetruereturn false;
truetrue} else if (next.x == -1) {
truetruetruegameOver();
truetruetruereturn false;
truetrue} else if (next.x == 21) {
truetruetruegameOver();
truetruetruereturn false;
truetrue}
true}
truefor (var len = obstacle.length, i = 0; i < len - 1; i++) { // 炸弹
truetrueif (next.x == obstacle[i].x && next.y == obstacle[i].y) {
truetruetruetruegameOver();
truetruetruetruereturn false;
truetruetrue}
true}
trueif (next.x == food.x && next.y == food.y) { // 检测到吃到食物
truetrueswitch (direction) {
truetruetruecase 0: snakeHead.y = snakeHead.y + 1; break;
truetruetruecase 1: snakeHead.y = snakeHead.y - 1; break;
truetruetruecase 2: snakeHead.x = snakeHead.x + 1; break;
truetruetruecase 3: snakeHead.x = snakeHead.x - 1; break;
truetrue}
truetrueeatFood();
true} else { // 正常移动
truetruevar len = snake.length;
truetruefor (var i = 4; i < len -1; i++) {
if (next.x == snake[i].x && next.y == snake[i].y) {
gameOver();
return false;
}
}
snake[len - 1].x = next.x;
snake[len - 1].y = next.y;
snake[0] = snake[len - 1];
snake.pop(snake[len - 1]);
rank();
change(snake[1]);
}
}
function rank() { // 排列数组,时刻保持snake[1]为蛇头
for (var len = snake.length; len > 0; len--) {
truetruesnake[len] = snake[len - 1];
true}
truesnake[0] = { };
}
function change(point) {
truepoint.div.style.left = point.x * nodLength + "px";
truepoint.div.style.top = point.y * nodLength + "px";
}
function snakePoint(x, y, div) {
truethis.x = x;
truethis.y = y;
truethis.direction = parseInt(Math.random() * 4);
truethis.div = div || initSnakePoint(this.x * nodLength, this.y * nodLength);
}
function randomFoodXY() { // 食物随机器
truevar isRepeat = true;
truewhile (isRepeat) {
truetrueresult = [parseInt(Math.random() * widthNum), parseInt(Math.random() * heightNum)];
truetrueisRepeat = false;
truetruefor (var len = snake.length, i = 1; i < len - 1; i++) { // 生成的食物是否与蛇重复
truetruetrueif (snake[i].x == result[0] && snake[i].y == result[1]) {
truetruetruetrueisRepeat = true;
truetruetruetruebreak;
truetruetrue}
truetrue}
truetrueif (!isRepeat) {
truetruetruefor (var len = obstacle.length, i = 0; i < len - 1; i++) { // 炸弹
truetruetruetrueif (result[0] == obstacle[i].x && result[1] == obstacle[i].y) {
truetruetruetruetrueisRepeat = true;
truetruetruetruetruebreak;
truetruetruetrue}
truetruetrue}
truetrue}
true}
truereturn result;
}
function randomObstacleXY() { // 障碍随机器
truevar isRepeat = true;
truewhile (isRepeat) { // 生成的障碍是否与蛇身重复
truetrueresult = [parseInt(Math.random() * widthNum), parseInt(Math.random() * heightNum)];
truetrueisRepeat = false;
truetruefor (var len = snake.length, i = 1; i < len - 1; i++) {
truetruetrueif (snake[i].x == result[0] && snake[i].y == result[1]) {
truetruetruetrueisRepeat = true;
truetruetruetruebreak;
truetruetrue}
truetrue}
true}
truereturn result;
}
function listenKey() { // 监听键盘
truedocument.onkeydown = keyEvent;
}
var key = true;
function keyEvent(e) { // 按键响应
trueif (key == true) {
truetruekey = false;
truetruee = e || window.event;
truetruevar keycode = e.which ? e.which :e.keyCode;
truetrueswitch (e.keyCode) {
truetruetruecase 37: if (snake[1].direction != 3) snake[1].direction = 2; break;
truetruetruecase 38: if (snake[1].direction != 1) snake[1].direction = 0; break;
truetruetruecase 39: if (snake[1].direction != 2) snake[1].direction = 3; break;
truetruetruecase 40: if (snake[1].direction != 0) snake[1].direction = 1; break;
truetrue}
true}
}
function eatFood() {
truesnake[0] = new snakePoint(food.x, food.y, food.div);
truesnake[0].div.className = "snakePoint";
truesnake[0].direction = snake[1].direction;
trueId("snakePoint").src = "./apple_snake.png";
truerank();
truescore += 1;
trueshowScore();
truecreateFood();
}
function gameOver() {
truevar name = prompt("得分:" + score + "\u000d请输入您的名字:(中文字母数字下划线)","游客");
trueif (name) {
truetrueId("nameBox").value = name;
truetrueId("scoreBox").value = score;
Id("submitScore").click();
true} else{ }
trueclearTimeout(timer);
trueremove(food.div);
truefor (var i = 1; i < snake.length; i++) {
remove(snake[i].div);
}
}
function showScore() {
Id("score").innerHTML = score;
if (score % 10 == 0 && speed >= 10) {
truetruespeed = speed - 20;
truetrueId("showSpeed").innerHTML = speed;
truetrueclearTimeout(timer);
truetruetimer = setInterval(run, speed);
true}
}
function reInitGame() { // 重新开始游戏
trueclearTimeout(timer);
trueId("pause").value = "暂停";
truevar div = document.getElementById("Box");
while (div.hasChildNodes()) {
div.removeChild(div.firstChild);
}
trueId("start").blur();
truesnake = [];
truesnake[0] = {};
truefood = {};
truescore = 0;
trueId("start").disabled = true;
trueId("pause").disabled = false;
trueId("reStart").style.display = ""
truevar level = document.getElementsByName("level");
truefor (var i = 0; i < level.length; i++) {
truetrueif (level[i].checked) {
truetruetruespeed = level[i].value;
truetruetrueId("showSpeed").innerHTML = speed;
truetruetruebreak;
truetrue}
true}
truetimer = window.setInterval(run, speed);
truesnake.push(new snakePoint(randomFoodXY()[0], randomFoodXY()[1]));
truecreateObstacle()
truecreateFood();
truelistenKey();
}
function enterSpeed() {
truevar myReg = /^[0-9]{1,4}$/;
trueif (!myReg.test(Id("speed").value)) {
truetrueId("showError").innerHTML = "0-9999";
truetrueId("showError").style.color = "red";
true} else {
truetrueclearTimeout(timer);
truetruespeed = Id("speed").value;
truetruetimer = setInterval(run, speed);
true}
}
function pause() {
trueif (Id("pause").value == "暂停") {
truetrueId("pause").value = "继续";
truetrueId("showSpeed").innerHTML = 0;
truetrueclearTimeout(timer);
true} else if (Id("pause").value == "继续") {
truetrueId("pause").value = "暂停";
truetrueId("showSpeed").innerHTML = speed;
truetruetimer = setInterval(run, speed);
true}
}
function remove(elem) {
trueif (elem) {
truetrueelem.parentNode.removeChild(elem);
true}
}
function Id(id) { // 公用,选取id
truereturn (typeof id == "string") ? document.getElementById(id) : id;
}
1
2
3
4
5
6
7
8
9
10
11
* {margin:0px;}
.snakePoint {width:30px; height:30px; position:absolute;}
img#snakePoint {width:30px; height:30px;}
.obstaclePoint {width:28px; height:28px; position:absolute;}
.foodPoint {width:30px; height:30px; position:absolute;}
.foodPoint img {width:30px; height:30px;}
#contain {margin:0 auto; width:860px; position:relative;}
#Box {border:4px solid black; width:630px; height:480px; position:relative;}
#Msg {border:4px yellow solid; width:230px;height:480px; position:absolute; left:650px; top:0px; padding-left:10px;}
.bold {font-weight:bolder; color:red;}
#cheer {padding:5px 2px; font-size:20px; color:red; display:none;}
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
<div id="contain">
truetrue<div id="Box">
truetrue</div>
truetrue<div id="Msg">
truetruetrue<input type="button" id="start" value="开始" onclick="initGame();" />
<input type="button" id="pause" disabled="true" value="暂停" onclick="pause();" />
<input type="button" id="reStart" value="重新开始" onclick="reInitGame();" style="display:none"/>
<br/><hr/>
truetruetrue穿墙:<input type="radio" name="through" value="yes" checked="checked">YES 
truetruetrue <input type="radio" name="through" value="no">NO 
<br/><hr/>
truetruetrue炸弹:<input type="radio" name="bombNum" value="3" checked="checked">3
truetruetrue <input type="radio" name="bombNum" value="5">5
truetruetrue <input type="radio" name="bombNum" value="10">10
truetruetrue <input type="radio" name="bombNum" value="15">15
truetruetrue <input type="radio" name="bombNum" value="20">20
truetruetrue <input type="radio" name="bombNum" value="30">30
truetruetrue <input type="radio" name="bombNum" value="50">50
truetruetrue <input type="radio" name="bombNum" value="70">70
truetruetrue <input type="radio" name="bombNum" value="100">100
truetrue <br/><hr/>
truetruetrue速度:(单位:ms)
truetruetrue<div style="padding:5px 2px;">
truetruetruetrue<input type="radio" name="level" value="50">50 
truetruetruetrue<input type="radio" name="level" value="75">75 
truetruetruetrue<input type="radio" name="level" value="100">100<br>
truetruetruetrue<input type="radio" name="level" value="150" checked="checked">150
truetruetruetrue<input type="radio" name="level" value="200">200
truetruetruetrue<input type="radio" name="level" value="300" >300<br>
truetruetruetrue<input type="text" name="level" value="20" id="speed" size="2"><input type="button" value="确定" onclick="enterSpeed()" ><span id="showError"></span>
<br><hr/>
truetruetrue</div>
truetruetrue速度:<span id="showSpeed" class="bold">0</span><br/>
truetruetrue分数:<span id="score" class="bold">0</span>
truetruetrue<p>说明:键盘↑、←、↓、→,控制蛇吃食物。每次十颗游戏加快20毫秒。</p>
truetruetrue<span>支持浏览器:FF3.6+,Chrome 8.0+,IE6+,Opera 10+,Safari 5+<span>
truetruetrue<p>BUG反馈,交流,留言:<a href="http://www.yekezhong.com/250">[亦疯实验室]JavaScript版贪吃蛇</a></p>
truetruetrue<p>超级无敌增强版 by 叶科忠</p>
truetrue</div>
truetrue<div style="height:100px; width:300px; overflow:scroll;">
truetrue <form action="" method="post" style="display:none">
truetruetrue<input type="text" name="name" value="游客" id="nameBox">
truetruetrue<input type="text" name="score" value="0" id="scoreBox">
truetruetrue<input type="submit" value="确定" id="submitScore">
truetrue </form>
truetrue <?php echo file_get_contents("./score.txt"); ?>
truetrue</div>
true</div>

CODE END.