前言
就是想写个简单的贪吃蛇,孩子玩,然后告诉他编程或者所谓的'玩电脑'也不光是纯玩儿,还是有知识的
思路
这个项目没有用到什么特别的js或者css特性
键盘事件
监听上下左右来修改方向,空格控制游戏开始
JS的类,存放蛇身体单元的基本信息,如坐标信息和颜色
CSS的grid布局,用来显示游戏画面的所有方块
代码实现
HTML
<h3>空格启动游戏,箭头控制方向。</h3>
<div class="box"></div>
太简单了没啥说的,box就是个最外层容器,里面会放很多div方块格子
CSS
body {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 0;
margin: 0;
}
.box {
width: 400px;
height: 400px;
border: 1px solid rgb(145, 145, 145);
background-color: rgb(145, 145, 145);
display: grid;
gap: 1px;
}
.cell {
background-color: white;
}
代码解释
body是为了让游戏容器居中
.box设置游戏容器的尺寸以及grid布局,可以让子元素整齐排列
.cell设置了单元格的背景色
JS
js还是比较复杂的,功能都在这里了,先上代码
let interval = null;
let box;
let rowCount = 30;
let columnCount = 30;
// 方向 top right bottom left
let direction = 'right';
// 单元格二维数组 0为空,20为果子,其他为蛇-正好可以用颜色值来代表
let cellArr = [];
// 蛇颜色
let snakeColor = 'pink';
// 蛇单元类
class SnakeClass {
// 构造函数
constructor(x, y, color) {
this.x = x;
this.y = y;
this.color = color;
}
// 设置单元格数组
setCellArr() {
cellArr[this.y][this.x] = this.color;
}
}
// 蛇数组
let snakeArr = [
new SnakeClass(2, 0, snakeColor),
new SnakeClass(1, 0, snakeColor),
new SnakeClass(0, 0, snakeColor),
];
// 果子对象
let apple = {
x: 0,
y: 0,
color: 'red',
show: true
};
// 初始函数
function init() {
box = document.querySelector('.box');
box.style.gridTemplateColumns = `repeat(${columnCount}, 1fr)`;
box.style.gridTemplateRows = `repeat(${rowCount}, 1fr)`;
createApplePosition();
createCell();
draw();
bindEvent();
}
// 游戏运行
function run() {
if(interval){
console.log('运行中');
return;
}
interval = setInterval(next, 200);
}
// 生成单元格二维数组
function createCell() {
cellArr = [];
for (let i = 0; i < columnCount; i++) {
let row = [];
for (let j = 0; j < rowCount; j++) {
row.push(0);
}
cellArr.push(row);
}
}
// 将蛇数组映射到单元格数组中
function mapSnake() {
for (let i = 0; i < snakeArr.length; i++) {
snakeArr[i].setCellArr();
}
}
// 将果子映射到单元格数组中
function mapApple() {
if (apple.show) {
cellArr[apple.y][apple.x] = 20;
}
}
// 绘制 根据cellArr生成单元格
function draw() {
// 清空容器
box.innerHTML = '';
mapSnake();
mapApple();
for (let i = 0; i < cellArr.length; i++) {
let row = cellArr[i];
for (let j = 0; j < row.length; j++) {
let cell = cellArr[i][j];
let cellDiv = document.createElement('div');
cellDiv.className = 'cell';
if (cell == 20) {
cellDiv.style.backgroundColor = apple.color;
} else if (cell == 0) {
} else {
cellDiv.style.backgroundColor = cell;
}
box.appendChild(cellDiv);
}
}
}
// 蛇走
function next() {
// 获取当前蛇头
let snakeHead = snakeArr[0];
// 计算下一步蛇头位置
let positionX = snakeHead.x, positionY = snakeHead.y;
switch (direction) {
case 'top':
positionY--;
break;
case 'right':
positionX++;
break;
case 'bottom':
positionY++;
break;
case 'left':
positionX--;
break;
}
//如果碰到墙壁就死了
if (checkDie(positionX,positionY)) {
die();
return;
}
// 添加蛇头
snakeArr.unshift(new SnakeClass(positionX, positionY, snakeColor));
// 判断是否吃到果子
if (positionX == apple.x && positionY == apple.y) {
// 吃到了,就不删蛇尾巴,重新生成果子位置
createApplePosition();
} else {
// 移除蛇尾
let last = snakeArr.pop();
// 对应的单元格也清空
cellArr[last.y][last.x] = 0;
}
draw();
}
// 生成果子位置
function createApplePosition() {
let ok = true;
while (ok) {
let exist = false;
let x = Math.floor(Math.random() * columnCount);
let y = Math.floor(Math.random() * rowCount);
for (let i = 0; i < snakeArr.length; i++) {
let item = snakeArr[i];
if (item.x == x && item.y == y) {
exist = true;
break;
}
}
if (!exist) {
ok = false;
apple.x = x;
apple.y = y;
}
}
}
// 判定死亡
function checkDie(x,y){
// 触碰边缘
if (y < 0 || y > (rowCount - 1) || x < 0 || x > (columnCount - 1)) {
return true;
}
// 触碰身体
for(let item of snakeArr){
if (x == item.x && y == item.y) {
return true;
}
}
return false;
}
// 死了
function die() {
alert("你死了");
// 重置蛇的位置
snakeArr = [
new SnakeClass(2, 0, snakeColor),
new SnakeClass(1, 0, snakeColor),
new SnakeClass(0, 0, snakeColor),
];
// 重生苹果位置
createApplePosition();
// 重置单元格
createCell();
// 停止定时器
clearInterval(interval);
interval = null;
draw();
}
// 是否反方向
function isOpposetDirection(keycode){
if(keycode=='ArrowUp' && direction=='bottom'){
return true;
}
if(keycode=='ArrowDown' && direction=='top'){
return true;
}
if(keycode=='ArrowLeft' && direction=='right'){
return true;
}
if(keycode=='ArrowRight' && direction=='left'){
return true;
}
return false;
}
//绑定按键事件
function bindEvent() {
window.addEventListener('keydown', function (e) {
if(isOpposetDirection(e.code)){
return;
}
switch (e.code) {
case 'ArrowUp':
direction = 'top';
break;
case 'ArrowDown':
direction = 'bottom';
break;
case 'ArrowLeft':
direction = 'left';
break;
case 'ArrowRight':
direction = 'right';
break;
case 'Space':
run();
break;
}
});
}
init();
代码解释
代码中的注释还是比较清楚的,这里说说一些关键的
cellArr,单元格数组,是一个二维数组,用来表示横竖的白色格子
snakeArr,蛇数组,是一个一维数组,里面包含多个SnakeClass实例
SnakeClass,蛇身体单元的类,包含x,y,color三个属性,一个setCellArr方法
x,y表示在cellArr中的坐标位置
color表示蛇身体单元的颜色
setCellArr方法用来将对应xy坐标的cellArr的元素改为蛇的颜色
apple,果子,蛇吃了长身体,包含xy坐标和果子颜色
run方法,里面有一个定时器,不断地触发next方法
next方法,根据当前方向,修改蛇头坐标,并判断是否吃到果子以及判定死亡(是否碰到自己的身体或游戏界面边缘)
createCell方法,根据给定的横竖数量,生成对应的二维数组
mapSnake方法,遍历蛇身体数组,将对应的坐标映射到cellArr中
mapApple方法,同mapSnake,只是映射的果子
draw方法,重绘单元格,先清空单元格,然后映射上面两个方法,遍历cellArr,判定对应元素是空/蛇/果
createApplePosition方法,随机生成果子的坐标
checkDie方法,判定是否死亡
die方法,死亡方法,重置所有变量并停止计时器
isOpposetDirection方法,判断按下的方向键是否和当前运行方向相反
总结
里面还有一些bug,比如玩两盘之后,按空格键直接报'你死了'可能是重置的变量有问题