1.首先创建游戏框
全部代码在最下方
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/vue@2/dist/vue.js"></script>
<style>
div {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
height: 100vh;
width: 100vw;
margin: 0;
padding: 0;
box-sizing: border-box;
}
#app {
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.gameBox {
width: 80%;
height: 80%;
border: 1px solid black;
}
.flexBox {
display: flex;
width: 100%;
}
.flexBox:last-of-type .inBox{
border-bottom: 0;
}
.inBox{
border: 1px solid #ebeaea;
border-left: 0;
border-top: 0;
}
.inBox:nth-of-type(100n){
border-right: 0;
}
</style>
</head>
<body>
<div id="app">
<div class="gameBox" ref="gameBox">
<div v-for="(item, index) in height" :key="index" class="flexBox">
<div v-for="(l, i) in width" :key="i+1000" :style="`width: ${boxwidth}px;height:${boxheight}px`" class="inBox"></div>
</div>
</div>
</div>
<script type="module">
new Vue({
el: '#app',
data: function () {
return {
boxwidth: 0, // 每个格子的宽
boxheight: 0, // 每个格子的高
width: 20, // 横着渲染多少个格子 调整表格宽修改这
height: 10, // 竖着渲染多少个格子 调整表格高修改这
}
},
methods: {
},
mounted() {
// 首先获取盒子的高宽
this.boxwidth = this.$refs.gameBox.offsetWidth / this.width
this.boxheight = this.$refs.gameBox.offsetHeight / this.height
}
})
</script>
</body>
</html>
区域讲解:
我将其分为了每行20块,每列10块的区域,首先我在 mounted 当中获取了游戏区域的宽高,并利用其宽高计算出了每个格子的长和宽
2.初始化网格
代码如下:
// 初始化
gameBegin() {
this.gameArr = []
this.snake = []
// 计算每个格子的值
for (let i = 0; i < this.height; i++) {
this.gameArr.push([])
for (let a = 0; a < this.width; a++) {
this.gameArr[i].push({
row: i + 1, // 第几行
col: a + 1, // 第几列
isHaveSnake: false, // 这块当中是否有蛇
isHaveFood: false, // 是否有食物
})
}
}
// 默认第一格有蛇
this.gameArr[0][0].isHaveSnake = true
this.snake.push(this.gameArr[0][0])
},
区域讲解:
初始化表格 gameArr 就是所有的格子数据 row 是第几行 col 为第几列 isHaveSnake 是这块当中是否为蛇块 isHaveFood 为是否是食物块
3.贪吃蛇移动
代码如下:
// 蛇移动函数
snakeMove() {
if (this.nowMoveDirection == 'bottom') {
if (this.snake[0].row + 1 > this.height) {
this.stop()
alert("游戏结束!")
return
}
this.snake.unshift({
row: this.snake[0].row + 1,
col: this.snake[0].col,
isHaveSnake: true,
isHaveFood: false,
})
}
if (this.nowMoveDirection == 'top') {
if (this.snake[0].row - 1 < 1) {
this.stop()
alert("游戏结束!")
return
}
this.snake.unshift({
row: this.snake[0].row - 1,
col: this.snake[0].col,
isHaveSnake: true,
isHaveFood: false,
})
}
if (this.nowMoveDirection == 'right') {
if (this.snake[0].col + 1 > this.width) {
this.stop()
alert("游戏结束!")
return
}
this.snake.unshift({
row: this.snake[0].row,
col: this.snake[0].col + 1,
isHaveSnake: true,
isHaveFood: false,
})
}
if (this.nowMoveDirection == 'left') {
if (this.snake[0].col - 1 < 1) {
this.stop()
alert("游戏结束!")
return
}
this.snake.unshift({
row: this.snake[0].row,
col: this.snake[0].col - 1,
isHaveSnake: true,
isHaveFood: false,
})
}
// 默认无视单位体积碰撞 打开此代码则碰撞游戏结束
// this.snake.forEach((item, index) => {
// if (index != 0 && item.row == this.snake[0].row && item.col == this.snake[0].col) {
// this.stop()
// alert("游戏结束!")
// }
// })
this.moveDirection = JSON.parse(JSON.stringify(this.nowMoveDirection))
// 如果吃到食物则不用去掉最后一个 否则去掉最后一个
if (!this.gameArr[this.snake[0].row - 1][this.snake[0].col - 1].isHaveFood) {
this.gameArr[this.snake[this.snake.length - 1].row - 1][this.snake[this.snake.length - 1].col - 1].isHaveSnake = false
this.snake.splice(this.snake.length - 1, 1)
} else {
this.snakeWidth++
this.randomFood()
}
this.snake.forEach((item, index) => {
if (index != 0 && item.row == this.snake[0].row && item.col == this.snake[0].col) {
this.stop()
alert("游戏结束!")
}
this.gameArr.forEach((l, i) => {
l.forEach((p, q) => {
if (item.row == p.row && item.col == p.col) {
this.gameArr[i][q].isHaveSnake = true
}
})
})
});
},
// 监听键盘
keyDown() {
document.onkeydown = (e) => {
//事件对象兼容
let e1 = e || event || window.event || arguments.callee.caller.arguments[0]
// 如果蛇长度大于1就不能往当前移动方向的相反方向移动
if (this.snakeWidth > 1) {
if (e1 && e1.keyCode == 37 && this.moveDirection == 'right') {
console.log("左 can not move");
return
}
if (e1 && e1.keyCode == 38 && this.moveDirection == 'bottom') {
console.log("上 can not move");
return
}
if (e1 && e1.keyCode == 39 && this.moveDirection == 'left') {
console.log("右 can not move");
return
}
if (e1 && e1.keyCode == 40 && this.moveDirection == 'top') {
console.log("下 can not move");
return
}
}
// 多按加速
// if (e1 && e1.keyCode == 37 && this.moveDirection == 'left') {
// this.snakeMove()
// }
// if (e1 && e1.keyCode == 38 && this.moveDirection == 'top') {
// this.snakeMove()
// }
// if (e1 && e1.keyCode == 39 && this.moveDirection == 'right') {
// this.snakeMove()
// }
// if (e1 && e1.keyCode == 40 && this.moveDirection == 'bottom') {
// this.snakeMove()
// }
//键盘按键判断:左箭头-37;上箭头-38;右箭头-39;下箭头-40
if (e1 && e1.keyCode == 37) {
// 按下左箭头
this.nowMoveDirection = "left"
console.log("左");
} else if (e1 && e1.keyCode == 39) {
// 按下右箭头
this.nowMoveDirection = "right"
console.log("右");
} else if (e1 && e1.keyCode == 38) {
// 按下右箭头
this.nowMoveDirection = "top"
console.log("上");
} else if (e1 && e1.keyCode == 40) {
// 按下右箭头
this.nowMoveDirection = "bottom"
console.log("下");
}
}
},
区域讲解:
本块是蛇在移动过程中,碰到边界则游戏结束,碰到自己的尾巴也是一样的还有吃到食物变长以及监听键盘上下左右移动事件
4.随机食物
代码如下:
// 随机食物位置
randomFood() {
let row = Math.floor(Math.random() * this.height) + 1
let col = Math.floor(Math.random() * this.width) + 1
this.gameArr.forEach((item, index) => {
item.forEach((l, i) => {
if (l.row == row && l.col == col && !l.isHaveSnake) {
// 生成新的随机食物时 将之前食物置空
this.gameArr.forEach((p, q) => {
p.forEach((k, j) => {
k.isHaveFood = false
})
})
this.gameArr[index][i].isHaveFood = true
}
})
})
},
区域讲解:
本块是随机生成食物块 在格子当中横纵坐标不为蛇块的地方生成食物块
5.总体代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/vue@2/dist/vue.js"></script>
<style>
div {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
height: 100vh;
width: 100vw;
margin: 0;
padding: 0;
box-sizing: border-box;
}
#app {
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.gameBox {
width: 80%;
height: 80%;
border: 1px solid black;
}
.flexBox {
display: flex;
width: 100%;
}
.flexBox:last-of-type .inBox {
border-bottom: 0;
}
.inBox {
border: 1px solid #ebeaea;
border-left: 0;
border-top: 0;
}
.inBox:nth-of-type(100n) {
border-right: 0;
}
.haveSnake {
background: rgb(114, 111, 111);
}
.haveFood {
background: #5ee252;
}
.posiBox {
position: absolute;
right: 6px;
top: 6px;
}
</style>
</head>
<body>
<div id="app">
<div class="posiBox">
<!-- <button @click="randomFood">随机食物测试</button> -->
<button @click="gameBegin">重新开始</button>
<button @click="again">继续</button>
<button @click="stop">暂停</button>
</div>
<div class="gameBox" ref="gameBox">
<div v-for="(item, index) in gameArr" :key="index" class="flexBox">
<div v-for="(l, i) in item" :key="i+1000" :style="`width: ${boxwidth}px;height:${boxheight}px`"
class="inBox" :class="l.isHaveSnake?'haveSnake':l.isHaveFood?'haveFood':''"></div>
</div>
</div>
</div>
<script type="module">
new Vue({
el: '#app',
data: function () {
return {
timer: "", // 蛇移动时的定时器 清除使用
boxwidth: 0, // 每个格子的宽
boxheight: 0, // 每个格子的高
width: 20, // 横着渲染多少个格子
height: 10, // 竖着渲染多少个格子
gameArr: [], // 渲染贪吃蛇的所有格子
snakeWidth: 1, // 贪吃蛇长度
snake: [], // 当前贪吃蛇占格
moveDirection: "bottom", // 之前移动方向
nowMoveDirection: "bottom", // 蛇默认往下走
isAgain: true, // 防抖
}
},
methods: {
// 随机食物位置
randomFood() {
let row = Math.floor(Math.random() * this.height) + 1
let col = Math.floor(Math.random() * this.width) + 1
this.gameArr.forEach((item, index) => {
item.forEach((l, i) => {
if (l.row == row && l.col == col && !l.isHaveSnake) {
// 生成新的随机食物时 将之前食物置空
this.gameArr.forEach((p, q) => {
p.forEach((k, j) => {
k.isHaveFood = false
})
})
this.gameArr[index][i].isHaveFood = true
}
})
})
},
// 初始化
gameBegin() {
this.gameArr = []
this.snake = []
// 计算每个格子的值
for (let i = 0; i < this.height; i++) {
this.gameArr.push([])
for (let a = 0; a < this.width; a++) {
this.gameArr[i].push({
row: i + 1, // 第几行
col: a + 1, // 第几列
isHaveSnake: false, // 这块当中是否有蛇
isHaveFood: false, // 是否有食物
})
}
}
// 默认第一格有蛇
this.gameArr[0][0].isHaveSnake = true
this.snake.push(this.gameArr[0][0])
this.keyDown()
this.again()
this.randomFood()
},
// 蛇移动函数
snakeMove() {
if (this.nowMoveDirection == 'bottom') {
if (this.snake[0].row + 1 > this.height) {
this.stop()
alert("游戏结束!")
return
}
this.snake.unshift({
row: this.snake[0].row + 1,
col: this.snake[0].col,
isHaveSnake: true,
isHaveFood: false,
})
}
if (this.nowMoveDirection == 'top') {
if (this.snake[0].row - 1 < 1) {
this.stop()
alert("游戏结束!")
return
}
this.snake.unshift({
row: this.snake[0].row - 1,
col: this.snake[0].col,
isHaveSnake: true,
isHaveFood: false,
})
}
if (this.nowMoveDirection == 'right') {
if (this.snake[0].col + 1 > this.width) {
this.stop()
alert("游戏结束!")
return
}
this.snake.unshift({
row: this.snake[0].row,
col: this.snake[0].col + 1,
isHaveSnake: true,
isHaveFood: false,
})
}
if (this.nowMoveDirection == 'left') {
if (this.snake[0].col - 1 < 1) {
this.stop()
alert("游戏结束!")
return
}
this.snake.unshift({
row: this.snake[0].row,
col: this.snake[0].col - 1,
isHaveSnake: true,
isHaveFood: false,
})
}
// 默认无视单位体积碰撞 打开此代码则碰撞游戏结束
// this.snake.forEach((item, index) => {
// if (index != 0 && item.row == this.snake[0].row && item.col == this.snake[0].col) {
// this.stop()
// alert("游戏结束!")
// }
// })
this.moveDirection = JSON.parse(JSON.stringify(this.nowMoveDirection))
// 如果吃到食物则不用去掉最后一个 否则去掉最后一个
if (!this.gameArr[this.snake[0].row - 1][this.snake[0].col - 1].isHaveFood) {
this.gameArr[this.snake[this.snake.length - 1].row - 1][this.snake[this.snake.length - 1].col - 1].isHaveSnake = false
this.snake.splice(this.snake.length - 1, 1)
} else {
this.snakeWidth++
this.randomFood()
}
this.snake.forEach((item, index) => {
if (index != 0 && item.row == this.snake[0].row && item.col == this.snake[0].col) {
this.stop()
alert("游戏结束!")
}
this.gameArr.forEach((l, i) => {
l.forEach((p, q) => {
if (item.row == p.row && item.col == p.col) {
this.gameArr[i][q].isHaveSnake = true
}
})
})
});
},
// 暂停
stop() {
// 防抖 如果游戏是继续的 才能暂停
if (!this.isAgain) {
this.isAgain = true
clearInterval(this.timer)
}
},
// 继续
again() {
// 防抖 如果游戏暂停 才能继续
if (this.isAgain) {
this.isAgain = false
this.timer = setInterval(() => {
this.snakeMove()
}, 1000)
}
},
// 监听键盘
keyDown() {
document.onkeydown = (e) => {
//事件对象兼容
let e1 = e || event || window.event || arguments.callee.caller.arguments[0]
// 如果蛇长度大于1就不能往当前移动方向的相反方向移动
if (this.snakeWidth > 1) {
if (e1 && e1.keyCode == 37 && this.moveDirection == 'right') {
console.log("左 can not move");
return
}
if (e1 && e1.keyCode == 38 && this.moveDirection == 'bottom') {
console.log("上 can not move");
return
}
if (e1 && e1.keyCode == 39 && this.moveDirection == 'left') {
console.log("右 can not move");
return
}
if (e1 && e1.keyCode == 40 && this.moveDirection == 'top') {
console.log("下 can not move");
return
}
}
// 多按加速
// if (e1 && e1.keyCode == 37 && this.moveDirection == 'left') {
// this.snakeMove()
// }
// if (e1 && e1.keyCode == 38 && this.moveDirection == 'top') {
// this.snakeMove()
// }
// if (e1 && e1.keyCode == 39 && this.moveDirection == 'right') {
// this.snakeMove()
// }
// if (e1 && e1.keyCode == 40 && this.moveDirection == 'bottom') {
// this.snakeMove()
// }
//键盘按键判断:左箭头-37;上箭头-38;右箭头-39;下箭头-40
if (e1 && e1.keyCode == 37) {
// 按下左箭头
this.nowMoveDirection = "left"
console.log("左");
} else if (e1 && e1.keyCode == 39) {
// 按下右箭头
this.nowMoveDirection = "right"
console.log("右");
} else if (e1 && e1.keyCode == 38) {
// 按下右箭头
this.nowMoveDirection = "top"
console.log("上");
} else if (e1 && e1.keyCode == 40) {
// 按下右箭头
this.nowMoveDirection = "bottom"
console.log("下");
}
}
},
},
mounted() {
// 首先获取盒子的高宽
this.boxwidth = this.$refs.gameBox.offsetWidth / this.width
this.boxheight = this.$refs.gameBox.offsetHeight / this.height
this.gameBegin()
},
destroy() {
this.stop()
}
})
</script>
</body>
</html>