純JS實現貪吃蛇遊戲 —— 可能是全網程式碼最優雅的實現。

玻璃窗起霧了發表於2021-01-18

在網上看了許多的貪吃蛇這類遊戲,效果基本還可以,功能也實現了,不過看程式碼大都是冗餘或雜亂不堪的,難以維護。

所以花了點時間,對整個遊戲重構了一下,也算是站在各位前輩的肩膀上做的最佳化,希望對大家有幫助。

功能描述

生成一條蛇,可以上下左右移動,目標只有一個:吃食物。吃到一個食物蛇的身體增加一節,然後生成下一個食物,撞到地圖就 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部落格 ” ,連結:http://blog.itpub.net/69992957/viewspace-2750856/,如需轉載,請註明出處,否則將追究法律責任。

相關文章