說在前面
在網上看了許多的貪吃蛇這類游戲,效果基本還可以,功能也實作了,不過看代碼大都是冗余或雜亂不堪的,難以維護,
所以花了點時間,對整個游戲重構了一下,也算是站在各位前輩的肩膀上做的優化,希望對大家有幫助,
效果圖.gif

功能描述
生成一條蛇,可以上下左右移動,目標只有一個:吃食物,吃到一個食物蛇的身體增加一節,然后生成下一個食物,撞到地圖就GG,game over,
設計思路
1. 整體實作采用原生JS,使用ES6的Class類構造,完美的詮釋了面向物件編程的編程思想,
2. js 主體檔案分成 food.js(食物類),snake.js(蛇類), game.js(游戲入口檔案),util.js(工具函式),講道理,地圖也需要用到一個map.js(地圖類),由于這里的地圖過分簡單,所以不搞也罷,
3. 設計思路圖解:

1. 工具類設計 ( util.js )
目標功能點:
- 生成隨機坐標
export function getRandom(a, b){
let max = Math.max(a, b);
let min = Math.min(a, b);
return parseInt(Math.random() * (max - min)) + min;
}
2. 食物類設計(food.js)
目標功能點:
- 初始化食物(寬,高,顏色等)
- 在地圖上隨機生成
- 管理食物(洗掉)
import { getRandom } from './util.js';
// 食物類
class Food {
// 初始化
constructor({x = 0, y = 0, width = 20, height = 20, color = 'green'} = {}){
// 結構賦值 引數默認值
// let options = {x = 0, y = 0, width = 20, height = 20, color = 'green'} || {};
// 存盤食物
this.elements = [];
// 坐標
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.color = color;
}
render(map){
this.remove(); // 洗掉之前創建的食物
// 隨機設定x,y的值
this.x = getRandom(0, map.offsetWidth / this.width - 10) * this.width;
this.y = getRandom(0, map.offsetHeight / this.height - 1) * this.height;
console.log(this.x, this.y);
// 創建食物 dom
let div = document.createElement('div');
map.appendChild(div);
this.elements.push(div);
// 設定div的樣式
div.style.position = 'absolute';
div.style.left = this.x + 'px';
div.style.top = this.y + 'px';
div.style.width = this.width + 'px';
div.style.height = this.height + 'px';
div.style.backgroundColor = this.color;
}
remove() {
// 從后往前
for(let i = this.elements.length -1; i >= 0; i--){
this.elements[i].parentNode.removeChild(this.elements[i]); // 洗掉div
this.elements.splice(i, 1); // 洗掉陣列中的元素
}
}
}
export default Food;
3. 蛇類(snake.js)
目標功能點:
- 初始化蛇(寬,高,顏色、長度等)
- 在地圖上初始定位
- 蛇的移動與管理(吃一個食物生成一個新的蛇物件)
- 判斷是否吃到食物(蛇頭的坐標與食物坐標重合)
// 蛇類
class Snake {
constructor({ width = 20, height = 20, direction = 'right' } = {}){
// 存盤蛇
this.elements = [];
this.width = width;
this.height = height;
this.direction = direction;
// 蛇的身體 初始三節
this.body = [
{x: 3, y: 2, color: 'red'},
{x: 2, y: 2, color: 'blue'},
{x: 1, y: 2, color: 'blue'},
];
}
render(map){
this.remove(); // 洗掉之前創建的蛇
for(let i = 0, len = this.body.length; i < len; i++ ){
let object = this.body[i];
let div = document.createElement('div');
map.appendChild(div);
this.elements.push(div);
// 設定樣式
div.style.position = 'absolute';
div.style.width = this.width + 'px';
div.style.height = this.height + 'px';
div.style.left = object.x * this.width + 'px';
div.style.top = object.y * this.height + 'px';
div.style.backgroundColor = object.color;
}
}
move(food, map){
// 控制蛇的移動 (當前蛇節 移動到上一個蛇節)
for(let i = this.body.length - 1; i > 0; i--){
this.body[i].x = this.body[i - 1].x;
this.body[i].y = this.body[i - 1].y;
}
// 蛇頭
let head = this.body[0];
// 蛇頭的行進方向
switch(this.direction) {
case 'right':
head.x += 1;
break;
case 'left':
head.x -= 1;
break;
case 'top':
head.y -= 1;
break;
case 'bottom':
head.y += 1;
break;
}
// 蛇吃食物
// 判斷蛇頭的位置是否與食物的位置重合
let headX = head.x * this.width;
let headY = head.y * this.height;
if(headX === food.x && headY === food.y){
let last = this.body[this.body.length -1 ];
this.body.push({
x: last.x,
y: last.y,
color: last.color
});
// 重新生成一個食物
food.render(map);
}
}
remove() {
for (let i = this.elements.length - 1; i >= 0; i--) {
// 洗掉div
this.elements[i].parentNode.removeChild(this.elements[i]);
// 洗掉陣列中的元素
this.elements.splice(i, 1);
}
}
}
export default Snake;
4. 游戲入口檔案(game.js)
目標功能點:
- 實體化蛇與食物
- 讓蛇動起來
- 系結按鍵,控制方向
- 開始游戲
- 當蛇撞到地圖邊緣GG,顯示 game over!
import Food from "./food.js";
import Snake from "./snake.js";
// 游戲的入口檔案
class Game {
constructor() {
// 創建食物和蛇的實體
this.food = new Food();
this.snake = new Snake();
this.map = map;
// 定時器
this.timerId = null;
}
start() {
// 食物和蛇 渲染到地圖上
this.food.render(this.map);
this.snake.render(this.map);
this.runSnake();
this.bindKey();
}
// 讓蛇動起來
runSnake() {
this.timerId = setInterval( () => {
// 要獲取游戲物件中的蛇屬性
this.snake.move(this.food, this.map);
// 2.2 當蛇遇到邊界游戲結束
var maxX = this.map.offsetWidth / this.snake.width;
var maxY = this.map.offsetHeight / this.snake.height;
var headX = this.snake.body[0].x;
var headY = this.snake.body[0].y;
if (headX < 0 || headX >= maxX || headY < 0|| headY >= maxY) {
console.log('Game Over');
clearInterval(this.timerId);
return
}
this.snake.render(this.map); // 根據body 的資料 重新渲染蛇在頁面位置
}, 150);
}
// 系結鍵盤事件 控制蛇的方向
bindKey() {
document.addEventListener('keydown', (e) => {
switch (e.keyCode) {
case 37:
this.snake.direction = 'left';
break;
case 38:
this.snake.direction = 'top';
break;
case 39:
this.snake.direction = 'right';
break;
case 40:
this.snake.direction = 'bottom';
break;
}
});
}
}
export default Game;
5. 呼叫(index.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="map" style="width:80%;height:400px;border: 1px solid orange;"></div>
<script type="module">
import Game from './game.js';
// 全域的地圖 map
let map = document.getElementById('map');
let game = new Game(map);
// 呼叫開始方法
game.start();
</script>
</body>
</html>
FAQ:
由于整個專案采用ES6的模塊設計,所以需要啟動一個本地服務才可以跑,單獨點開index.html,是沒得用的,
如果您老這個游戲玩的開心,學到了新的東西,可以關注、點贊、收藏、轉發、評論一波,您的鼓勵是我創作的最大動力!
以后將為各位客官保持優質內容的持續輸出,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/249919.html
標籤:其他
上一篇:【Vue3 造輪子專案 】kaiteUI中利用Custom Block(自定義塊)和vite實作代碼渲染器
下一篇:筆記(第一周)
