JavaScript贪吃蛇游戏实现详解

作者:交互手记时间:2026-10-24 15:27:05浏览:363

本文详细介绍使用JavaScript实现经典贪吃蛇游戏的完整过程。内容涵盖HTML结构搭建、CSS样式布局以及核心JS逻辑,包括键盘事件监听、边界与碰撞检测、食物随机生成及蛇身跟随算法。文中特别解析了定时器冲突、数组索引越界等常见开发陷阱及解决方案,适合前端初学者参考学习。

本文分享了一段使用JavaScript编写的贪吃蛇游戏完整代码,供读者参考学习。

在编码之前,需要明确贪吃蛇游戏应具备的核心功能模块。

首先,游戏需支持通过键盘的方向键(上、下、左、右)来控制蛇的移动方向。

其次,必须实现边界检测机制:当蛇头移动至游戏区域边界之外时,判定为游戏结束。

第三,需实现碰撞检测逻辑:当蛇头与食物块发生位置重叠时,触发特定事件。

第四,当蛇吃到食物后,游戏得分应增加1分。

以下是具体的代码实现部分。

HTML结构部分。


<div class="container">
        <!--小蛇移动的操场-->
        <div id="playground">
            <!--小蛇-->
            <div id="snake"></div>
            <!--食物-->
            <div id="food"></div>
        </div>
        <!--记录得分-->
        <div id="menu">
            <div>得分<span id="score"></span></div>
        </div>
</div>

CSS样式部分。


<style>
        * {
            padding: 0;
            margin: 0;
        }
 
        .container {
            width: 800px;
            height: 600px;
            margin: 0 auto;
        }
 
        #playground {
            width: 650px;
            height: 100%;
            background-color: cadetblue;
            float: left;
            position: relative;
        }
 
        #menu {
            width: 150px;
            height: 100%;
            background-color: darkcyan;
            float: left;
        }
 
        #snake {
            width: 20px;
            height: 20px;
            background-color: #d3e0d7;
            position: absolute;
            left: 0;
            top: 0;
        }
 
        #food {
            width: 20px;
            height: 20px;
            background-color: #027766;
            position: absolute;
        }
 
        .body {
            width: 20px;
            height: 20px;
            background-color: #178b9a;
            position: absolute;
            ;
            top: 0;
            left: 0;
        }
 
        #score {
            font-size: 30px;
            font-weight: bold;
            color: black;
        }
 
        #menu div {
            font-size: 20px;
            font-weight: bold;
            margin-left: 20px;
        }
 
        #hqx {
            width: 100px;
            height: 50px;
            margin: 0 auto;
 
        }
 
        #inp {
            border: 0;
            width: 100px;
            height: 50px;
            background-color: coral;
        }
</style>

JavaScript核心功能实现部分。

第一步是获取页面中的DOM节点,并定义所需的全局变量。


//获取节点
        var snake = document.getElementById("snake");
        var food = document.getElementById("food");
        var playground = document.getElementById("playground");
        var score = document.getElementById('score');
        // var inp = document.getElementById('inp')
        /*设置全局变量*/
        var timer;
        var arr = []; //存放蛇的位置的数组
        var num = 0; //数组的长度
        var snakeBody; //每次吃调一个食物,增加的身体

第二步是设置键盘事件监听,并处理边界碰撞逻辑,若发生越界则游戏结束。在实现过程中,曾遇到使用if语句配合return跳出事件会导致后续代码无法执行的问题,最终改用switch-case结构并配合break语句解决了该逻辑阻塞问题。


 //设置按键事件
        document.onkeydown = function (e) {
            var evt = window.evnet || e;
            switch (evt.keyCode) {
                case 37: //左
                    clearInterval(timer);
                    timer = window.setInterval(runLeft, 10); //向左移动的时间器
                    function runLeft() {
                        if (snake.offsetLeft > 0) {
                            snake.style.left = snake.offsetLeft - 1 + 'px'; //实现自己动
                            snake.style.top = snake.offsetTop + 'px'; //高度不变
                            arr.push([snake.offsetLeft, snake.offsetTop]); //每移动1px,就将位置存进数组中
                            num++; //相应的数组长度加1
                        } else {
                            clearInterval(timer); //清除定时器
                            alert('you die') //弹出失败信息
                            document.onkeydown = null //结束按键
                        }
                    }
                    break; //结束当前按键
                case 38: //上
                    clearInterval(timer);
                    timer = window.setInterval(runTop, 10);
 
                    function runTop() {
                        if (snake.offsetTop > 0) {
                            snake.style.top = snake.offsetTop - 1 + 'px';
                            snake.style.left = snake.offsetLeft + 'px';
                            arr.push([snake.offsetLeft, snake.offsetTop]);
                            num++;
                        } else {
                            clearInterval(timer);
                            alert('you die')
                            document.onkeydown = null
                        }
                    }
                    break;
                case 39: //右
                    clearInterval(timer);
                    timer = window.setInterval(runRight, 10);
 
                    function runRight() {
                        if (snake.offsetLeft < 630) {
                            snake.style.left = snake.offsetLeft + 1 + 'px';
                            snake.style.top = snake.offsetTop + 'px';
                            arr.push([snake.offsetLeft, snake.offsetTop]);
                            num++;
                        } else {
                            clearInterval(timer);
                            alert('you die')
                            document.onkeydown = null
                        }
                    }
                    break;
                case 40: //下
                    clearInterval(timer);
                    timer = window.setInterval(runBottom, 10);
 
                    function runBottom() {
                        if (snake.offsetTop < 580) {
                            snake.style.top = snake.offsetTop + 1 + 'px';
                            snake.style.left = snake.offsetLeft + 'px';
                            arr.push([snake.offsetLeft, snake.offsetTop]);
                            num++;
                        } else {
                            clearInterval(timer);
                            alert('you die')
                            document.onkeydown = null
                        }
                    }
                    break;
 
}

第三步是封装一个函数,用于随机生成食物的位置。在初次编写时,因遗漏了CSS单位导致样式未生效,经排查后补全了单位设置。


function pos() {
                food.style.left = parseInt(Math.random() * 630) + 'px';
                food.style.top = parseInt(Math.random() * 580) + 'px';
            }

第四步是封装碰撞判定函数。当检测到蛇头与食物重合时,使食物消失并增加蛇身长度。此处曾遇到定时器管理问题:由于清除定时器的标识符与创建定时器的标识符不一致,导致蛇身出现上下左右抖动现象,经过多次调试修正定时器逻辑后解决了该Bug。


var timer1 = setInterval(eat, 10); //设置一个碰撞的时间器
            function eat() {
                snakeCrashFood(snake, food); //传入参数
 
                function snakeCrashFood(obj1, obj2) {
                    var obj1Left = obj1.offsetLeft;
                    var obj1Width = obj1.offsetWidth + obj1.offsetLeft;
                    var obj1Top = obj1.offsetTop;
                    var obj1Height = obj1.offsetHeight + obj1.offsetTop;
                    var obj2Left = obj2.offsetLeft;
                    var obj2Width = obj2.offsetWidth + obj2.offsetLeft;
                    var obj2Top = obj2.offsetTop;
                    var obj2Height = obj2.offsetHeight + obj2.offsetTop;
                    if (!((obj1Width < obj2Left) || (obj2Width < obj1Left) || (obj1Height < obj2Top) || (
                            obj2Height < obj1Top))) {
                        snakeBody = document.createElement("div"); //生成新的div
                        snakeBody.setAttribute("class", "body"); //给新的div添加类名
                        playground.appendChild(snakeBody); //添加一节新的身体
                        pos(); //让食物重新随机出现
                        setInterval(follow, 10); //动态地改变新的身体的位置
                    }
                }
            }

第五步是实现蛇身跟随逻辑。通过获取蛇身数组,计算新身体段相对于上一身体段的位置(偏移量为20)。初期因初始num值为0且place为20,导致数组索引从负数开始从而引发越界错误。在导师指导下,增加了索引有效性判定逻辑,成功解决了该问题。


function follow() {
                /*获得增加的身体的数组*/
                var bodyNum = document.getElementsByClassName("body");
                score.innerHTML = bodyNum.length;
                var place = 0;
                /*数组每移动1px,新的身体的位置就是相对于前一个身体的第20个数组的位置,后面依次加等*/
                // console.log("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1")
                // console.log("arr   :  ==" + arr)
                // console.log("num : ==" + num)  //2
                for (var i = 0; i < bodyNum.length; i++) {
                    // console.log("bodyNum.length :  ==" + bodyNum.length)
                    place += 20;
                    // console.log("place :  ==" + place)//20
                    // console.log("num - place :  ==" + (num - place))//-18
                    // bodyNum[i].style.left = arr[num - place][0] + 'px';
                    // bodyNum[i].style.top = arr[num - place][1] + 'px';
                    if (num - place > 0) {
                        bodyNum[i].style.left = arr[num - place][0] + 'px';
                        bodyNum[i].style.top = arr[num - place][1] + 'px';
                    }
 
                }
                // console.log("num555 : ==" + num)
                // console.log("===========================================")
            }

以上就是本文的全部内容,希望对读者的学习有所帮助,也欢迎大家继续支持本站。

作者最新文章