在网上看了许多的贪吃蛇这类游戏,效果基本还可以,功能也实现了,不过看代码大都是冗余或杂乱不堪的,难以维护。
所以花了点时间,对整个游戏重构了一下,也算是站在各位前辈的肩膀上做的优化,希望对大家有帮助。
功能描述
生成一条蛇,可以上下左右移动,目标只有一个:吃食物。吃到一个食物蛇的身体增加一节,然后生成下一个食物,撞到地图就 GG , game over 。
设计思路
1. 整体实现采用原生 JS ,使用 ES6 的 Class 类构造,完美的诠释了面向对象编程的编程思想。
2. JS 主体文件分成 food.js(食物类),snake.js(蛇类), game.js(游戏入口文件),util.js(工具函数)。讲道理,地图也需要用到一个map.js(地图类), 站长交易 由于这里的地图过分简单,所以不搞也罢。
1. 工具类设计 ( util.js )
目标功能点:
· 生成随机坐标
1. export function getRandom(a, b){
2. let max = Math.max(a, b);
3. let min = Math.min(a, b);
4. return parseInt(Math.random() * (max - min)) + min;
5. }
2. 食物类设计( food.js )
目标功能点:
· 初始化食物(宽,高,颜色等)
· 在地图上随机生成
· 管理食物(删除)
1. import { getRandom } from './util.js';
2. // 食物类
3. class Food {
4. // 初始化
5. constructor({x = 0, y = 0, width = 20, height = 20, color = 'green'} = {}){
6. // 结构赋值 参数默认值
7. // let options = {x = 0, y = 0, width = 20, height = 20, color = 'green'} || {};
8. // 存储食物
9. this.elements = [];
10. // 坐标
11. this.x = x;
12. this.y = y;
13.
14. this.width = width;
15. this.height = height;
16. this.color = color;
17. }
18.
19. render(map){
20. this.remove(); // 删除之前创建的食物
21.
22. // 随机设置 x,y 的值
23. this.x = getRandom(0, map.offsetWidth / this.width - 10) * this.width;
24. this.y = getRandom(0, map.offsetHeight / this.height - 1) * this.height;
25. console.log(this.x, this.y);
26.
27. // 创建食物 dom
28. let div = document.createElement('div');
29. map.appendChild(div);
30. this.elements.push(div);
31.
32. // 设置 div 的样式
33. div.style.position = 'absolute';
34. div.style.left = this.x + 'px';
35. div.style.top = this.y + 'px';
36. div.style.width = this.width + 'px';
37. div.style.height = this.height + 'px';
38. div.style.backgroundColor = this.color;
39. }
40. remove() {
41. // 从后往前
42. for(let i = this.elements.length -1; i >= 0; i--){
43. this.elements[i].parentNode.removeChild(this.elements[i]); // 删除 div
44. this.elements.splice(i, 1); // 删除数组中的元素
45. }
46. }
47. }
48. export default Food;
3. 蛇类( snake.js )
目标功能点:
· 初始化蛇(宽,高,颜色、长度等)
· 在地图上初始定位
· 蛇的移动与管理(吃一个食物生成一个新的蛇对象)
· 判断是否吃到食物(蛇头的坐标与食物坐标重合)
1. // 蛇类
2. class Snake {
3. constructor({ width = 20, height = 20, direction = 'right' } = {}){
4. // 存储蛇
5. this.elements = [];
6.
7. this.width = width;
8. this.height = height;
9. this.direction = direction;
10. // 蛇的身体 初始三节
11. this.body = [
12. {x: 3, y: 2, color: 'red'},
13. {x: 2, y: 2, color: 'blue'},
14. {x: 1, y: 2, color: 'blue'},
15. ];
16. }
17.
18. render(map){
19. this.remove(); // 删除之前创建的蛇
20. for(let i = 0, len = this.body.length; i < len; i++ ){
21. let object = this.body[i];
22.
23. let div = document.createElement('div');
24. map.appendChild(div);
25. this.elements.push(div);
26.
27. // 设置样式
28. div.style.position = 'absolute';
29. div.style.width = this.width + 'px';
30. div.style.height = this.height + 'px';
31. div.style.left = object.x * this.width + 'px';
32. div.style.top = object.y * this.height + 'px';
33. div.style.backgroundColor = object.color;
34. }
35. }
36.
37. move(food, map){
38. // 控制蛇的移动 ( 当前蛇节 移动到上一个蛇节 )
39. for(let i = this.body.length - 1; i > 0; i--){
40. this.body[i].x = this.body[i - 1].x;
41. this.body[i].y = this.body[i - 1].y;
42. }
43. // 蛇头
44. let head = this.body[0];
45.
46. // 蛇头的行进方向
47. switch(this.direction) {
48. case 'right':
49. head.x += 1;
50. break;
51. case 'left':
52. head.x -= 1;
53. break;
54. case 'top':
55. head.y -= 1;
56. break;
57. case 'bottom':
58. head.y += 1;
59. break;
60. }
61.
62. // 蛇吃食物
63. // 判断蛇头的位置是否与食物的位置重合
64. let headX = head.x * this.width;
65. let headY = head.y * this.height;
66.
67. if(headX === food.x && headY === food.y){
68. let last = this.body[this.body.length -1 ];
69. this.body.push({
70. x: last.x,
71. y: last.y,
72. color: last.color
73. });
74. // 重新生成一个食物
75. food.render(map);
76. }
77. }
78. remove() {
79. for (let i = this.elements.length - 1; i >= 0; i--) {
80. // 删除 div
81. this.elements[i].parentNode.removeChild(this.elements[i]);
82. // 删除数组中的元素
83. this.elements.splice(i, 1);
84. }
85. }
86. }
87.
88. export default Snake;
4. 游戏入口文件( game.js )
目标功能点:
· 实例化蛇与食物
· 让蛇动起来
· 绑定按键,控制方向
· 开始游戏
· 当蛇撞到地图边缘 GG ,显示 game over !
1. import Food from "./food.js";
2. import Snake from "./snake.js";
3. // 游戏的入口文件
4. class Game {
5. constructor() {
6. // 创建食物和蛇的实例
7. this.food = new Food();
8. this.snake = new Snake();
9. this.map = map;
10. // 定时器
11. this.timerId = null;
12. }
13. start() {
14. // 食物和蛇 渲染到地图上
15. this.food.render(this.map);
16. this.snake.render(this.map);
17. this.runSnake();
18. this.bindKey();
19.
20. }
21. // 让蛇动起来
22. runSnake() {
23. this.timerId = setInterval( () => {
24. // 要获取游戏对象中的蛇属性
25. this.snake.move(this.food, this.map);
26. // 2.2 当蛇遇到边界游戏结束
27. var maxX = this.map.offsetWidth / this.snake.width;
28. var maxY = this.map.offsetHeight / this.snake.height;
29. var headX = this.snake.body[0].x;
30. var headY = this.snake.body[0].y;
31. if (headX < 0 || headX >= maxX || headY < 0|| headY >= maxY) {
32. console.log('Game Over');
33. clearInterval(this.timerId);
34. return
35. }
36. this.snake.render(this.map); // 根据 body 的数据 重新渲染蛇在页面位置
37. }, 150);
38. }
39. // 绑定键盘事件 控制蛇的方向
40. bindKey() {
41. document.addEventListener('keydown', (e) => {
42. switch (e.keyCode) {
43. case 37:
44. this.snake.direction = 'left';
45. break;
46. case 38:
47. this.snake.direction = 'top';
48. break;
49. case 39:
50. this.snake.direction = 'right';
51. break;
52. case 40:
53. this.snake.direction = 'bottom';
54. break;
55. }
56. });
57. }
58. }
59. export default Game;
5. 调用( index.html )
1. <!DOCTYPE html>
2. <html>
3.
4. <head>
5. <meta charset="UTF-8">
6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
7. <title>Document</title>
8. </head>
9.
10. <body>
11. <div id="map" style="width:80%;height:400px;border: 1px solid orange;"></div>
12. <script type="module">
13. import Game from './game.js';
14. // 全局的地图 map
15. let map = document.getElementById('map');
16. let game = new Game(map);
17. // 调用开始方法
18. game.start();
19. </script>
20. </body>
21.
22. </html>
FAQ :
由于整个项目采用 ES6 的模块设计,所以需要启动一个本地服务才可以跑,单独点开 index.html ,是没得用的。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69992957/viewspace-2750856/ ,如需转载,请注明出处,否则将追究法律责任。