前一陣用vue寫了一個貪喫蛇,主要練習一下自己的邏輯拆分能力,在此與大家分享一下。
此貪喫蛇可以通過分辨率比例自動計算格子數量,用二維數組實現。
<<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
.tetris-wrapper {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: #fff;
overflow: auto;
}
.game-wrapper {
position: absolute;
top: 0.1rem;
bottom: 0.1rem;
left: 0.1rem;
right: 0.1rem;
display: flex;
flex-direction: column;
background-color: grey;
}
.block-group {
flex: 1;
position: relative;
display: flex;
}
.block {
flex: 1;
border: 0.01rem solid #000;
}
.green {
background-color: green;
}
.red {
background-color: red;
}
.blue {
background-color: blue;
}
.black {
background-color: black;
}
</style>
</head>
<body>
<div id="game"
class="tetris-wrapper"
@click="test"
ref="test"
@swipeup="changeMoveDirection(0)"
@swiperight="changeMoveDirection(1)"
@swipedown="changeMoveDirection(2)"
@swipeleft="changeMoveDirection(3)">
<div class="game-wrapper" ref="container">
<div class="block-group" v-for="(item, index) in structureArray" :key="index">
<div class="block"
v-for="(block, blockIndex) in item"
:key="blockIndex"
:class="{green: block.state === 1, red: block.state === 2, blue: block.state === 3, black: block.state === 4}">
</div>
</div>
</div>
</div>
</body>
<script>
let app = new Vue ({
el: '#game',
data () {
return {
timer: null, // 存儲定時器
interval: 200, // 定時間隔時間
transverse: 20, // 橫座標方格數量
structureArray: [], // 遊戲畫布結構數組
moveDirection: 1, // 蛇的移動方向,0、1、2、3,上右下左
nextMoveDirection: 1, // 將要移動的方向,0、1、2、3,上右下左
snakeHead: {}, // 蛇頭的座標
snakeTail: {}, // 蛇尾座標
foodCoordinate: {} // 食物座標
}
},
methods: {
test () {
console.log(this.$refs.container.clientHeight);
console.log(this.$refs.container.clientWidth);
console.log(this.longitudinal);
},
changeMoveDirection (direction) { // 改變蛇的運動方向
let judge = direction - this.moveDirection; // 判斷非法的蛇頭轉向
if (judge === 2 || judge === -2) return;
this.nextMoveDirection = direction;
},
mobileRule () { // 蛇的移動規則定義
let {x, y} = this.snakeHead; // 當前蛇頭的座標
let headCode = this.structureArray[y][x].code; // 獲取當前蛇頭的code,用來生成新蛇頭時++使用
this.structureArray[y][x].state = 1; // 取消原來的蛇頭狀態
switch(this.nextMoveDirection) { // 移動方向,計算移動後的橫縱座標
case 0:
y--;
break;
case 1:
x++
break;
case 2:
y++;
break;
case 3:
x--;
break;
default:
throw new Error('蛇的移動規則判斷出錯');
break;
}
this.structureArray[y].splice(x, 1, {state: 2, code: ++headCode}); // 生成新的蛇頭
this.snakeHead = {x, y};
this.moveDirection = this.nextMoveDirection;
this.whetherEatFood(); // 判斷是否喫到食物(蛇頭和食物座標是否重合)
},
whetherEatFood () { // 是否喫到食物判斷
let state = JSON.stringify(this.snakeHead) === JSON.stringify(this.foodCoordinate); // 判斷食物和蛇頭是否在一個座標上
if (state) { // 喫到了
this.generateFood(); // 生成食物
this.integralRule(); // 走積分
this.increaseDifficulty(); // 增加難度
} else { // 沒喫到食物
this.generateSnakeTail(); // 重新生成蛇尾
}
},
generateFood () { // 生成食物,生成隨機食物座標
let x = Math.floor(Math.random() * this.transverse); // 生成隨機數
let y = Math.floor(Math.random() * this.longitudinal); // 生成隨機數
if (this.structureArray[y][x].state === 0) { // 是空位置
this.foodCoordinate = {x, y}; // 新的食物座標
this.structureArray[y].splice(x, 1, { state: 4, code: Infinity}); // 修改座標狀態值,code無限大用來計算蛇尾時不會出錯
} else { // 位置不爲空,重新生成
this.generateFood();
}
},
integralRule () { // 積分規則
},
increaseDifficulty () { // 增加難度規則
if (this.interval <= 100) return;
this.interval -= 4;
this.clearTimer();
this.initializationTimer();
},
generateSnakeTail () { // 重新生成蛇尾(在沒有喫到食物的情況下調用)
let {x, y} = this.snakeTail; // 獲取蛇尾座標
// let tailCode = this.structureArray[y][x].code + 1; // 計算將要作爲蛇尾的code -------- 這裏code取錯了,這樣的話取得是蛇頭的,改成取上下左右最小的code
// 銷燬之前的蛇尾(若當前蛇尾蛇尾位置是蛇頭,則不做處理,因爲頭先生成)
if (this.structureArray[y][x].state !== 2) this.structureArray[y].splice(x, 1, {state: 0});
// 接下來重新定位蛇尾座標
/*if (this.structureArray[y + 1][x].state && tailCode === this.structureArray[y + 1][x].code) y++;
else if (this.structureArray[y - 1][x].state && tailCode === this.structureArray[y - 1][x].code) y--;
else if (this.structureArray[y][x + 1].state && tailCode === this.structureArray[y][x + 1].code) x++;
else if (this.structureArray[y][x - 1].state && tailCode === this.structureArray[y][x - 1].code) x--;*/
// else throw new Error('重新生成蛇尾錯誤')
// 取上下左右code最小的是新蛇尾
let top = (y - 1 >= 0) && this.structureArray[y - 1][x].state ? this.structureArray[y - 1][x].code : Infinity;
let bottom = (y + 1 < this.longitudinal) && this.structureArray[y + 1][x].state ? this.structureArray[y + 1][x].code : Infinity;
let left = (x - 1 >= 0) && this.structureArray[y][x - 1].state ? this.structureArray[y][x - 1].code : Infinity;
let right = (x + 1 < this.transverse) && this.structureArray[y][x + 1].state ? this.structureArray[y][x + 1].code : Infinity;
let min = Math.min(top, bottom, left, right);
switch(min) {
case top:
y--;
break;
case bottom:
y++;
break;
case left:
x--;
break;
case right:
x++;
break;
default:
console.log('重新生成蛇尾出錯了');
break;
}
this.snakeTail = {x, y}; // 保存蛇尾座標
this.structureArray[y][x].state = 3; // 將此座標定義爲蛇尾
},
deathRule () { // 蛇的死亡規則判斷
let {x, y} = this.snakeHead;
switch (this.nextMoveDirection) { // 移動方向(碰壁判斷和碰蛇身判斷)
case 0:
if (y - 1 < 0) return false;
else if (this.structureArray[y - 1][x].state === 1) return false;
break;
case 1:
if (x + 1 > this.transverse - 1) return false;
else if (this.structureArray[y][x + 1].state === 1) return false;
break;
case 2:
if (y + 1 > this.longitudinal - 1) return false;
else if (this.structureArray[y + 1][x].state === 1) return false;
break;
case 3:
if (x - 1 < 0) return false;
else if (this.structureArray[y][x - 1].state === 1) return false;
break;
default:
throw new Error('蛇的死亡規則判斷錯誤');
break;
}
return true;
},
deathTreatment () { // 蛇的死亡處理
this.clearTimer();
alert('你的小寶貝沒了!!!!');
},
initializationGrid () { // 初始化網格
this.structureArray = [];
for (let i = 0; i < this.longitudinal; i++) {
this.structureArray.push([]);
for (let j = 0; j < this.transverse; j++) {
this.structureArray[i].push({
state: 0 // 0爲空,1爲蛇身,2爲蛇頭,3爲蛇尾,4爲食物
});
}
}
},
initializationSnake () { // 初始化蛇
this.structureArray[2].splice(4, 1, {state: 2, code: 2});
this.structureArray[2].splice(3, 1, {state: 1, code: 1});
this.structureArray[2].splice(2, 1, {state: 3, code: 0});
this.snakeHead = {x: 4, y: 2}; // 蛇頭座標
this.snakeTail = {x: 2, y: 2}; // 蛇尾座標
},
initializationTimer () { // 初始化定時器
this.timer = setInterval(() => {
// 處理
if (this.deathRule()) { // 死不了
this.mobileRule(); // 走移動
} else { // 死掉了
this.deathTreatment();
}
}, this.interval);
},
clearTimer () { // 清理定時器
clearInterval(this.timer);
},
addKeyboardEvents () { // 添加全局鍵盤按下事件
document.onkeydown = (event) => {
console.log(event);
switch(event.keyCode) {
case 38:
this.changeMoveDirection(0);
break;
case 39:
this.changeMoveDirection(1);
break;
case 40:
this.changeMoveDirection(2);
break;
case 37:
this.changeMoveDirection(3);
break;
}
}
}
},
async mounted () {
await this.initializationGrid(); // 初始化網格
this.initializationSnake(); // 初始化蛇
this.initializationTimer(); // 初始化定時器
this.generateFood(); // 生成食物
this.addKeyboardEvents(); // 添加鍵盤按下事件
},
destroyed () {
this.clearTimer();
},
computed: {
longitudinal () { // 獲取容器寬高比,計算縱座標方格數
let boxWidth = this.$refs.container.clientWidth;
let boxHeight = this.$refs.container.clientHeight;
let longitudinal = parseInt((boxHeight / boxWidth) * this.transverse);
return longitudinal;
}
}
});
</script>
</html>