/**
* 大致思路:
* 1. 创建一个长宽为500的画布
* 2. 创建一个长宽为25的方格
* 3. 计算出一行(一列)有多少格子
* 4. 食物 :使用随机数(乘以最大格子数)
* 5. 蛇 : 蛇的头部是随机数,如果吃了(食物)增长部分是蛇的(这次的位置)
* 6. 蛇的移动 : 通过重新定义的addPlace{r ++ | --,c ++ | --} push进snake数组(重新绘制画面,从而改变蛇的位置)
* 7. 蛇吃食物 : 蛇头移动到食物的位置(一样,不执行删除)
* 8. 蛇撞墙 : 蛇头移动到墙的位置(执行Pause函数)
* 9. 蛇撞自己 : 蛇头移动到蛇的尾巴的位置(执行Pause函数)
* 10. 上下左右: 通过按钮key值判断向那个前进(不能按相反的按键)
* 刚开始随机方向前进(通过判断)
* 11. 撞到自己: 循环身体判断头部 r 和 c 是否一样(是则失败)
*/
css
<style>
* {
padding: 0;
margin: 0
}
body {
height: 100vh;
overflow: hidden;
background-color: rgb(247, 90, 37);
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
#canvas {
background: #fefcfc;
}
.contain__footer {
display: flex;
}
button,
.text {
margin: 1rem;
min-width: 5rem;
height: 2.5rem;
border-radius: 3vmin;
background: rgb(245, 205, 112);
}
.text {
display: flex;
justify-content: center;
align-items: center;
font-size: 1.5rem;
color: #000;
font-size: 1rem;
min-width: 8rem;
}
</style>
html
<div class="contain__footer">
<div class="text"></div>
<button class="begin">开始</button>
<button class="pause">暂停</button>
</div>
<canvas id="canvas"></canvas>
Js
<script>
/**
* 大致思路:
* 1. 创建一个长宽为500的画布
* 2. 创建一个长宽为25的方格
* 3. 计算出一行(一列)有多少格子
* 4. 食物 :使用随机数(乘以最大格子数)
* 5. 蛇 : 蛇的头部是随机数,如果吃了(食物)增长部分是蛇的(这次的位置)
* 6. 蛇的移动 : 通过重新定义的addPlace{r ++ | --,c ++ | --} push进snake数组(重新绘制画面,从而改变蛇的位置)
* 7. 蛇吃食物 : 蛇头移动到食物的位置(一样,不执行删除)
* 8. 蛇撞墙 : 蛇头移动到墙的位置(执行Pause函数)
* 9. 蛇撞自己 : 蛇头移动到蛇的尾巴的位置(执行Pause函数)
* 10. 上下左右: 通过按钮key值判断向那个前进(不能按相反的按键)
* 刚开始随机方向前进(通过判断)
* 11. 撞到自己: 循环身体判断头部 r 和 c 是否一样(是则失败)
*/
// 一个小盒子 长宽
let gridWH = 25;
// 整个盒子的长宽
let boxWH = 500;
const canvas = document.getElementById('canvas');
canvas.width = boxWH;
canvas.height = boxWH;
const ctx = canvas.getContext('2d');
// 一行 有多少个盒子
let rowColumn = boxWH / gridWH;
// 计时器 存放
let timing = null
// 初始食物位置
let food = {
r: Math.floor(Math.random() * rowColumn),
c: Math.floor(Math.random() * rowColumn),
}
// 贪吃蛇
let snake = [{
r: Math.floor(Math.random() * rowColumn),
c: Math.floor(Math.random() * rowColumn),
}]
// 随机转向函数
const getDivert = () => {
let head = snake.at(-1)
let randomDire = Math.floor(Math.random() * 2)
if (head.r > rowColumn / 2) {
if (food.c > rowColumn / 2) {
return randomDire ? 'up' : 'left'
} else {
return randomDire ? 'down' : 'left'
}
} else {
if (food.c > rowColumn / 2) {
return randomDire ? 'up' : 'right'
} else {
return randomDire ? 'down' : 'right'
}
}
}
// 转向
let divert = getDivert()
// 显示器, 进行中 | 已暂停 | 已结束
let state = document.querySelector('.text')
// 绘制贪吃蛇 // 食物 图片
const drawSnake = () => {
const img = new Image();
img.src = 'https://ts1.cn.mm.bing.net/th/id/R-C.ce37e3aeba0235882a4cbce008740f50?rik=1KQIPHuVZasYmw&riu=http%3a%2f%2fimage.huahuibk.com%2fuploads%2f20190228%2f22%2f1551363575-xTatlDnCwi.jpg&ehk=Yaculj3h0W%2fRw2n0uImmsIUVv9kzHg%2bP7spQwzw8xzk%3d&risl=&pid=ImgRaw&r=0';
// 食物位置
if (img.src) {
if (food.r == snake[snake.length - 1].r && food.c == snake[snake.length - 1].c) {
food = {
r: Math.floor(Math.random() * rowColumn),
c: Math.floor(Math.random() * rowColumn),
}
img.onload = function () {
ctx.drawImage(img, food.r * gridWH, food.c * gridWH, gridWH, gridWH)
}
return
}
img.onload = function () {
ctx.drawImage(img, food.r * gridWH, food.c * gridWH, gridWH, gridWH)
}
} else {
ctx.fillRect(food.r * gridWH, food.r * gridWH, gridWH, gridWH)
}
}
// 盒子
const box = () => {
// 绘制表格
for (let i = 0; i < rowColumn; i++) {
ctx.beginPath();
ctx.moveTo(i * gridWH, 0);
ctx.lineTo(i * gridWH, boxWH);
ctx.lineWidth = 1;
ctx.stroke();
}
for (let i = 0; i < rowColumn; i++) {
ctx.beginPath();
ctx.moveTo(0, i * gridWH);
ctx.lineTo(boxWH, i * gridWH);
ctx.lineWidth = 1;
ctx.stroke();
}
// 绘制食物
drawSnake();
// 要移除的坐标
let removePlaceX = snake[0].r
let removePlaceY = snake[0].c
// 贪吃蛇头部
let head = snake[snake.length - 1]
// 不和snake数组关联
let addPlace = { r: 0, c: 0 }
if (divert == 'up') addPlace = { r: head.r, c: head.c - 1 }
if (divert == 'down') addPlace = { r: head.r, c: head.c + 1 }
if (divert == 'right') addPlace = { r: head.r + 1, c: head.c }
if (divert == 'left') addPlace = { r: head.r - 1, c: head.c }
// 当大于等于盒子格子数 | 小于 0 => 游戏结束
if (head.c < 0 || head.c >= rowColumn || head.r < 0 || head.r >= rowColumn) {
clickPause("撞墙了,刷新重新开始")
return
} else {
impact(addPlace)
snake.push(addPlace)
if (food.r != addPlace.r || food.c != addPlace.c) {
if (food.r == head.r && food.c == head.c) return
snake.shift()
ctx.clearRect(removePlaceX * gridWH, removePlaceY * gridWH, gridWH, gridWH);
}
// 图片
for (let i = 0; i < snake.length; i++) {
ctx.fillStyle = i == snake.length - 1 ? 'rgb(70, 108, 40)' : 'rgb(170, 209, 10)'
ctx.fillRect(snake[i].r * gridWH, snake[i].c * gridWH, gridWH, gridWH)
}
}
}
// 开始
document.querySelector('.begin').addEventListener('click', () => {
timing = setInterval(box, 300)
state.textContent = "进行中"
})
// 失败
const clickPause = (value) => {
clearInterval(timing)
state.textContent = `${value}`
}
document.querySelector('.pause').addEventListener('click', () => {
clearInterval(timing)
state.textContent = `暂停,点击开始继续`
})
// 上下左右
document.addEventListener('keydown', (e) => {
if (e.key == 'ArrowUp' && divert != 'down') divert = 'up'
if (e.key == 'ArrowDown' && divert != 'up') divert = 'down'
if (e.key == 'ArrowLeft' && divert != 'right') divert = 'left'
if (e.key == 'ArrowRight' && divert != 'left') divert = 'right'
})
// 是否撞到自己
const impact = (addPlace) => {
for (let j = 0; j < snake.length; j++) {
if (addPlace.r == snake[j].r && addPlace.c == snake[j].c) {
clickPause(`撞到自己人了,刷新重新开始`)
}
}
}
// 初始值
box()
</script>
有更多想法请评论,感谢了