前言

就是想写个简单的贪吃蛇,孩子玩,然后告诉他编程或者所谓的'玩电脑'也不光是纯玩儿,还是有知识的

HTML+CSS+JS纯手写贪吃蛇_html贪吃蛇

思路

这个项目没有用到什么特别的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,比如玩两盘之后,按空格键直接报'你死了'可能是重置的变量有问题