前一阵用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>