实例分享之JavaScript实现贪吃蛇小游戏

本篇文章给大家带来了利用javascript实现贪吃蛇小游戏的实例,希望对大家有帮助。

实例分享之JavaScript实现贪吃蛇小游戏

JavaScript实现贪吃蛇小游戏

功能概述

本程序实现了如下功能:

贪吃蛇的基本功能

统计得分

立即学习“Java免费学习笔记(深入)”;

开始与暂停

选择难度等级

设置快捷键

5.1 通过ijkl,wsad也能实现方向的切换

5.2 通过“P” 表示暂停,“C”表示开始或继续,”R”表示重新开始

实现过程

最开始的实现原理其实是参考的csdn的一位大神,他用JavaScript20行就实现了贪吃蛇的基本功能,难等可贵的是还没有bug,链接在此

要实现贪吃蛇大概有以下几个步骤:

画一个蛇的移动区域

画一条蛇

画食物

让蛇动起来

设定游戏规则

设置难度等级

设置开始与暂停

设置游戏结束后续操作

实现人机交互页面

注:下面的过程讲解部分只是讲述了部分原理与实现,建议一边看最后的完整代码,一边看下面的讲解,更容易理解每一部分的原理与实现

画蛇的活动区域

首先我们画蛇的活动区域,我们采用JavaScript的canvas进行绘制

我们用一个400 × 400 400\times 400400×400的区域作为蛇的活动区域


同时通过CSS设置一个边界线

#canvas {    border: 1px solid #000000; /* 设置边框线 */}

画蛇和食物

效果如下:

image-20211105183534687

在画蛇前我们需要想下蛇的数据结构,在这里我们采取最简单的队列表示蛇

队首表示蛇头位置,队尾表示蛇尾位置

我们将之前画的                                      400                          ×                          400                                 400\times 400                    400×400区域划分为400个                                      20                          ×                          20                                 20\times 20                    20×20的方块,用这些方块组成蛇,那么蛇所在方块的位置的取值范围就是0~399

举个例子:

var snake=[42,41,40];

上述代码表示蛇所在的位置为42,41,40三个方块,其中蛇头为42,蛇尾为40

对于食物,我们可以用一个变量food存储食物的位置即可,食物的取值范围为0~399,且不包括蛇的部分,由于游戏中需要随机产生食物,随机函数实现如下:

// 产生min~max的随机整数,用于随机产生食物的位置function random(min, max) {    const num = Math.floor(Math.random() * (max - min)) + min;    return num;}

当食物被蛇吃掉后就需要重新刷新食物,由于食物不能出现在蛇所在的位置,我们用一个while循环,当食物的位置不在蛇的数组中则跳出循环

while (snake.indexOf((food = random(0, 400))) >= 0); // 重新刷新食物,注意食物应不在蛇内部

我们接下来通过canvas进行绘制

首先在js中获取canvas组件

const canvas = document.getElementById("canvas");const ctx = canvas.getContext("2d");

然后写绘制函数用于绘制方格,绘制方格的时候注意我们预留1px作为边框,即我们所画的方格的边长为18,我们实际填充的是18 × 18 18\times 1818×18的方块,方块的x、y坐标(方块的左上角)的计算也需要注意加上1px

注:canvas的原点坐标在左上角,往右为x轴正方向,往下为y轴正方向

// 用于绘制蛇或者是食物代表的方块,seat为方块位置,取值为0~399,color为颜色function draw(seat, color) {    ctx.fillStyle = color; // 填充颜色    // fillRect的四个参数分别表示要绘制方块的x坐标,y坐标,长,宽,这里为了美观留了1px用于边框    ctx.fillRect(        (seat % 20) * 20 + 1,        Math.floor(seat / 20) * 20 + 1,        18,        18    );}

让蛇动起来

我们要想让蛇动起来,首先要规定蛇运动的方向,我们用一个变量direction来表示蛇运动的方向,其取值范围为{1,-1,20,-20},1 表示向右,-1 表示向左,20 表示向下,-20 表示向上,运动时只需要将蛇头的位置加上direction就可以表示新蛇头的位置,这样我们就可以表示蛇的运动了。

那么如何让蛇动起来呢,对于蛇的每次移动,我们需要完成下面几个基本操作:

将蛇运动的下一个位置变成新蛇头将下一个位置加入蛇队列绘制下一个方块为浅蓝色把旧蛇头变成蛇身绘制旧蛇头为浅灰色删除旧蛇尾将旧蛇尾弹出蛇队列绘制旧蛇尾位置为白色

当蛇吃掉食物时(蛇的下一个位置为食物所在位置)则需更新食物的位置,并绘制新食物为黄色,此时则不需要删除旧蛇尾,这样可以实现蛇吃完食物后长度的增加功能

我们需要写一个函数实现上述操作,并且要不断地调用这个函数,从而实现页面的刷新,即我们所说的动态效果

n = snake[0] + direction; // 找到新蛇头坐标snake.unshift(n); // 添加新蛇头draw(n, "#1a8dcc"); // 绘制新蛇头为浅蓝色draw(snake[1], "#cececc"); // 将原来的蛇头(浅蓝色)变成蛇身(浅灰色)if (n == food) {    while (snake.indexOf((food = random(0, 400))) >= 0); // 重新刷新食物,注意食物应不在蛇内部    draw(food, "Yellow"); // 绘制食物} else {    draw(snake.pop(), "White"); // 将原来的蛇尾绘制成白色}

接下来,我们需要实现通过键盘控制蛇的运动

我们需要获取键盘的key值,然后通过一个监听函数去监听键盘按下的操作,我们这里通过上下左右键(还拓展了WSAD键和IJKL键控制上下左右方向),同时设置一个变量n表示下一步的方向

// 用于绑定键盘上下左右事件,上下左右方向键,代表上下左右方向document.onkeydown = function (event) {    const keycode = event.keyCode;    if (keycode <= 40) {        // 上 38 下 40 左 37 右 39        n = [-1, -20, 1, 20][keycode - 37] || direction; // 若keycode为其他值,即表示按了没用的键,则方向不变    } else if (keycode = 73) {        // i 73 j 74 k 75 l 76        n = [-20, -1, 20, 1][keycode - 73] || direction;    } else {        switch (keycode) {            case 87: //w                n = -20;                break;            case 83: //s                n = 20;                break;            case 65: //a                n = -1;                break;            case 68: //d                n = 1;                break;            default:                n = direction;        }    }    direction = snake[1] - snake[0] == n ? direction : n; // 若方向与原方向相反,则方向不变};

设定游戏规则

贪吃蛇的最基础的游戏规则如下:

蛇如果撞到墙或者蛇的身体或尾巴则游戏结束蛇如果吃掉食物则蛇的长度会增加(上一步已经实现)且得分会增加

先实现第一个,具体如下:

注:下面的一段代码中的n即为新蛇头的位置

// 判断蛇头是否撞到自己或者是否超出边界if (    snake.indexOf(n, 1) > 0 ||    n  399 ||    (direction == 1 && n % 20 == 0) ||    (direction == -1 && n % 20 == 19)) {    game_over();}

接下来我们实现得分统计,对于得分的计算我们只需要设置一个变量score,用于统计得分,然后每吃一个食物,该变量加一,然后将得分信息更新到网页相应位置

score = score + 1;score_cal.innerText = "目前得分: " + score; // 更新得分

设置难度等级

我们在网页上设置一个单选框,用于设置难度等级

    难度等级:                         

效果如下:

image-20211105193208392

那么我们后台具体如何设置难度等级的功能呢?

ViiTor实时翻译 ViiTor实时翻译

AI实时多语言翻译专家!强大的语音识别、AR翻译功能。

ViiTor实时翻译 116 查看详情 ViiTor实时翻译

我们采取调用蛇运动的函数的时间间隔来代替难度,时间间隔越小则难度越大,我们分三级:简单、中级、困难

我们创建一个时间间隔变量time_internal,然后用一个函数获取单选框的取值,并将相应模式的时间间隔赋值给time_internal

// 用刷新间隔代表蛇的速度,刷新间隔越长,则蛇的速度越慢const simply_mode = 200;const middle_mode = 100;const hard_mode = 50;var time_internal = simply_mode; // 刷新时间间隔,用于调整蛇的速度,默认为简单模式// 同步难度等级function syncMode() {    var mode_value = "";    for (var i = 0; i < mode_item.length; i++) {        if (mode_item[i].checked) {            mode_value = mode_item[i].value;        }    }    switch (mode_value) {        case "simply":            time_internal = simply_mode;            break;        case "middle":            time_internal = middle_mode;            break;        case "hard":            time_internal = hard_mode;            break;    }}

最后只需要在蛇每次移动前调用一次上述函数syncMode()就可以实现难度切换,至于蛇的速度的具体调节且看下面如何讲解

设置开始与暂停

如何实现蛇的移动动态效果,如何暂停,如何继续,速度如何调节,这就涉及到JavaScript的动画的部分了,建议看下《JavaScript高级程序设计(第4版)》第18章的部分,讲的很详细。

在最初的“20行JavaScript实现贪吃蛇”中并没有实现开始与暂停,其实现动态效果的方法为设置一个立即执行函数!function() {}();,然后在该函数中使用setTimeout(arguments.callee, 150);,每隔0.15秒调用此函数,从而实现了网页的不断刷新,也就是所谓的动态效果。

后来,我通过web课程老师的案例(弹球游戏)中了解到requestAnimationFrame方法可以实现动画效果,于是我便百度查询,最后在翻书《JavaScript高级程序设计(第4版)》第18章动画与Canvas图形中得到启发–如何实现开始与取消,如何自定义时间间隔(实现难度调节,蛇的速度)

书中给出的开始动画与取消动画的方法如下:

注:为了便于理解,自己修改过原方法

var requestID; // 用于标记请求ID与取消动画function updateProgress() { // do something...    requestID = requestAnimationFrame(updateProgress); // 调用后在函数中反复调用该函数} id = requestAnimationFrame(updateProgress); // 第一次调用(即开始动画)cancelAnimationFrame(requestID); // 取消动画

书中讲述道:

requestAnimationFrame()已经解决了浏览器不知道 JavaScript 动画何时开始的问题, 以及最佳间隔是多少的问题。······

传给 requestAnimationFrame()的函数实际上可以接收一个参数,此参数是一个 DOMHighResTimeStamp 的实例(比如 performance.now()返回的值),表示下次重绘的时间。这一点非常重要: requestAnimationFrame()实际上把重绘任务安排在了未来一个已知的时间点上,而且通过这个参数 告诉了开发者。基于这个参数,就可以更好地决定如何调优动画了。

requestAnimationFrame()返回一个请求 ID,可以用于通过另一个 方法 cancelAnimationFrame()来取消重绘任务

书中同样给出了如何控制时间间隔的方法:

书中讲述道:

配合使用一个计时器来限制重绘操作执行的频率。这样,计时器可以限制实际的操作执行间隔,而 requestAnimationFrame 控制在浏览器的哪个渲染周期中执行。下面的例子可以将回调限制为不超过 50 毫秒执行一次

具体方法如下:

let enabled = true; function expensiveOperation() { console.log('Invoked at', Date.now()); } window.addEventListener('scroll', () => {  if (enabled) {      enabled = false;      requestAnimationFrame(expensiveOperation);      setTimeout(() => enabled = true, 50);  } });

由上面的方法我得到启发,在此处我们可以设置一个控制函数,用于控制隔一定的时间调用一次蛇运动的函数,实现如下:

// 控制游戏的刷新频率,每隔time_internal时间间隔刷新一次function game_control(){    if(enabled){        enabled = false;        requestAnimationFrame(run_game);        setTimeout(() => enabled = true, time_internal);    }    run_id = requestAnimationFrame(game_control);}// 启动或继续游戏function run_game() {    syncMode(); // 同步难度等级    n = snake[0] + direction; // 找到新蛇头坐标    snake.unshift(n); // 添加新蛇头    // 判断蛇头是否撞到自己或者是否超出边界    if (        snake.indexOf(n, 1) > 0 ||        n  399 ||        (direction == 1 && n % 20 == 0) ||        (direction == -1 && n % 20 == 19)    ) {        game_over();    }    draw(n, "#1a8dcc"); // 绘制新蛇头为浅蓝色    draw(snake[1], "#cececc"); // 将原来的蛇头(浅蓝色)变成蛇身(浅灰色)    if (n == food) {        score = score + 1;        score_cal.innerText = "目前得分: " + score; // 更新得分        while (snake.indexOf((food = random(0, 400))) >= 0); // 重新刷新食物,注意食物应不在蛇内部        draw(food, "Yellow"); // 绘制食物    } else {        draw(snake.pop(), "White"); // 将原来的蛇尾绘制成白色    }    // setTimeout(arguments.callee, time_internal); //之前的方案,无法实现暂停和游戏的继续}

至于暂停只需要在特定的位置调用cancelAnimationFrame(run_id);就可以了

设置游戏结束后续操作

我想的是在游戏结束后出现一个“弹窗”,显示最终得分和是否再来一把

效果如下:

image-20211105203816613

首先,我们实现网页的弹窗,通过调研发现JavaScript的弹窗可以通过alert()的方法实现,不过在网页上直接弹窗感觉不太美观,而且影响体验,于是我想了一下,可以采用一个p标签实现伪弹窗,在需要显示的时候设置其display属性为block,不需要显示的时候设置其display属性为none,就类似于Photoshop里面的图层概念,这样我们就可以在平常的时候设置其display属性为none触发game over时设置其display属性为block,实现如下:

游戏结束!

您的最终得分为: 0分

其CSS部分如下:

#game_over {    display: none; /* 设置game over 窗口不可见 */    position: fixed;    top: 190px;    left: 65px;    width: 280px;    height: 160px;    background-color: aliceblue;    border-radius: 5px;    border: 1px solid #000; /* 设置边框线 */}#once_again {    position: relative;    left: 20px;}#cancel {    position: relative;    left: 50px;}

接下来,我们需要实现game over的后续操作:暂停动画,显示得分,显示“弹窗”

function game_over(){    cancelAnimationFrame(run_id);    game_over_score.innerText = "您的最终得分为: " + score + "分";    game_over_p.style.display = "block";}

实现人机交互页面

接下来的部分就是提高用户体验的部分,具体实现下列功能/操作

游戏说明人机交互按钮:开始/继续,暂停,重新开始快捷键由于在游戏过程中通过鼠标移动到暂停键暂停,时间上太慢,可能造成游戏终止,故应该设置开始/继续(C)、暂停(P)、重新开始(R)的快捷键有些电脑键盘的上下左右键较小,操作起来不太方便,可以添加WSAD或者IJKL扩展,用于控制上下左右方向

效果如下:

image-20211105205627819

至于写界面的代码,可以看文末的完整代码,这里就稍微讲解下绑定按键点击事件与绑定快捷键

我们首先看下绑定按键点击事件,点击”开始/继续“只需要调用requestAnimationFrame(game_control);,点击”暂停“只需要调用cancelAnimationFrame(run_id);

// 绑定开始按钮点击事件start_btn.onclick = function () {    run_id = requestAnimationFrame(game_control);};// 绑定暂停按钮点击事件pause_btn.onclick = function () {    cancelAnimationFrame(run_id);};

点击“重新开始”的话,则需要先暂停动画,然后删除画面上的蛇和食物,初始化所有设置,然后再调用requestAnimationFrame(game_control);开始游戏

注:初始化时需要初始化得分与难度等级,这里解释下为什么要将第一个食物设置为蛇头下一个位置,因为这样的话蛇会自动先吃一个食物,继而可以通过“开始 / 继续” 一个按钮实现开始和继续操作,同时run_game()函数中的食物绘制是在蛇吃到食物之后,保证第一个食物顺利绘制,这样的话score就需要初始化为-1

// 用于初始化游戏各项参数function init_game() {    snake = [41, 40];     direction = 1;     food = 42;    score = -1;     time_internal = simply_mode;    enabled = true;    score_cal.innerText = "目前得分: 0分"; // 更新得分    mode_item[0].checked = true; // 重置难度等级为简单}// 绑定重新开始按钮点击事件restart_btn.onclick = function () {    cancelAnimationFrame(run_id);    // 将原有的食物和蛇的方块都绘制成白色    for(var i = 0; i < snake.length; i++){        draw(snake[i], "White");    }    draw(food, "White");    // 初始化游戏各项参数    init_game();    run_id = requestAnimationFrame(game_control);};

接下来,我们绑定game over中的两个按键”再来一把“和”取消“

”再来一把“只需要完成“重新开始”里面的事件即可,”取消“只需要完成”重新开始“点击操作中除了开始游戏的部分,即除了run_id = requestAnimationFrame(game_control);

这两个按钮都需要设置”弹窗“的display属性为none

具体实现如下:

// 绑定游戏结束时的取消按钮点击事件cancel_btn.onclick = function () {    for(var i = 0; i < snake.length; i++){        draw(snake[i], "White");    }    draw(food, "White");    init_game();    game_over_p.style.display = "none";}// 绑定游戏结束时的再来一把按钮点击事件once_again_btn.onclick = function () {    for(var i = 0; i < snake.length; i++){        draw(snake[i], "White");    }    draw(food, "White");    init_game();    game_over_p.style.display = "none";    run_id = requestAnimationFrame(game_control);}

最后,我们来讲解下如何设置快捷键,快捷键只需要用JavaScript模拟点击对应的按钮即可,实现如下:

// 同时绑定R 重启,P 暂停,C 继续document.onkeydown = function (event) {    const keycode = event.keyCode;    if(keycode == 82){        // R 重启        restart_btn.onclick();    } else if(keycode == 80){        // P 暂停        pause_btn.onclick();    } else if(keycode == 67){        // C 继续        start_btn.onclick();    } };

问题、调试与解决

注: 此部分为本人在实现过程中出现的bug、调试过程以及解决方法,感兴趣的可以看看,不感兴趣的也可以跳过此部分,直接看文末的完整代码

问题1:点击暂停和开始,游戏正常开始,按P也可以实现暂停,按C则画面出现蛇所在的方格乱跳,无法正常开始,但是按C的操作中只模拟了”开始 / 继续“按钮的点击?

效果如下:

image-20211105212956276

调试过程:因为蛇头的位置是由direction控制的,故想到设置断点,同时监测这个变量的值的变化,发现这个值在按完P和C时被更新成很大的数,进而去找direction在哪里被更新,发现点击P或C后还需要执行下面这一行代码,而实际上是不需要的

direction = snake[1] - snake[0] == n ? direction : n; // 若方向与原方向相反,则方向不变

解决方法:只需要执行完对应的模拟鼠标点击相应按钮事件之后就直接return就可以了

原代码与修改后的代码如下:

document.onkeydown = function (event) {    const keycode = event.keyCode;    if(keycode == 82){        // R 重启        restart_btn.onclick();        return; // 后来加上的    } else if(keycode == 80){        // P 暂停        pause_btn.onclick();        return; // 后来加上的    } else if(keycode == 67){        // C 继续        start_btn.onclick();        return; // 后来加上的    } else if (keycode <= 40) {        // 上 38 下 40 左 37 右 39        n = [-1, -20, 1, 20][keycode - 37] || direction; // 若keycode为其他值,则方向不变    } else if (keycode = 73) {        // i 73 j 74 k 75 l 76        n = [-20, -1, 20, 1][keycode - 73] || direction;    } else {        switch (keycode) {            case 87: //w                n = -20;                break;            case 83: //s                n = 20;                break;            case 65: //a                n = -1;                break;            case 68: //d                n = 1;                break;            default:                n = direction;        }    }    direction = snake[1] - snake[0] == n ? direction : n; // 若方向与原方向相反,则方向不变};

问题2:调整难度等级后,蛇的速度并没有发生改变,但是通过console.log()发现确实调用了同步难度模式的函数?

调试过程:在同步难度等级的函数中设置console.log()方法,输出time_internal变量,同时设断点调试,发现time_internal变量不发生变化,mode_value变量始终为undefined,最后发现应该是值传递时的错误mode_value = mode_item.value;

解决方法:修改值传递的方法,加上索引,改为mode_value = mode_item[i].value;

原代码和修改后的代码如下:

// 同步难度等级function syncMode() {    var mode_value = "";    for (var i = 0; i < mode_item.length; i++) {        if (mode_item[i].checked) {            mode_value = mode_item[i].value;//原来是mode_item.value        }    }    switch (mode_value) {        case "simply":            time_internal = simply_mode;            break;        case "middle":            time_internal = middle_mode;            break;        case "hard":            time_internal = hard_mode;            break;    }}

完整代码

                  贪吃蛇小游戏    button {  width: 100px;  height: 40px;  font-weight: bold;}#game_title {  margin-left: 95px;}#canvas {  border: 1px solid #000000; /* 设置边框线 */}#score {  font-weight: bold;}#mode_form {  font-weight: bold;}#game_over {  display: none; /* 设置game over 窗口不可见 */  position: fixed;  top: 190px;  left: 65px;  width: 280px;  height: 160px;  background-color: aliceblue;  border-radius: 5px;  border: 1px solid #000; /* 设置边框线 */}#once_again {  position: relative;  left: 20px;}#cancel {  position: relative;  left: 50px;}            

贪吃蛇小游戏

游戏结束!

您的最终得分为: 0分


游戏说明:

1. 用键盘上下左右键(或者IJKL键,或者WSAD键)控制蛇的方向,寻找吃的东西
2. 每吃一口就能得到一定的积分,同时蛇的身子会越吃越长
3. 不能碰墙,不能咬到自己的身体,更不能咬自己的尾巴
4. 在下方单选框中选择难度等级,点击"开始 / 继续"即开始游戏,点击"暂停"则暂停游戏,
再点击"开始 / 继续"继续游戏,点击"重新开始"则重新开始游戏
5. 快捷键: "C"表示开始或继续,"P"表示暂停,"R"表示重新开始

目前得分: 0分

难度等级:
const canvas = document.getElementById("canvas");const ctx = canvas.getContext("2d");const start_btn = document.getElementById("startButton");const pause_btn = document.getElementById("pauseButton");const restart_btn = document.getElementById("restartButton");const once_again_btn = document.getElementById("once_again");const cancel_btn = document.getElementById("cancel");const game_over_p = document.getElementById("game_over");const game_over_score = document.getElementById("game_over_score");const score_cal = document.getElementById("score");const mode_item = document.getElementsByName("mode");// 用刷新间隔代表蛇的速度,刷新间隔越长,则蛇的速度越慢const simply_mode = 200;const middle_mode = 100;const hard_mode = 50;//注意要改为var const是不会修改的var snake = [41, 40]; // 蛇身体队列var direction = 1; // 方向:1为向右,-1为向左,20为向下,-20为向上var food = 42; // 食物位置,取值为0~399var n; // 蛇的下一步的方向(由键盘和蛇的原方向决定)var score = -1; // 得分var time_internal = simply_mode; // 刷新时间间隔,用于调整蛇的速度,默认为简单模式let enabled = true; // 用于控制是否刷新,实现通过一定频率刷新let run_id; // 请求ID,用于暂停功能// 产生min~max的随机整数,用于随机产生食物的位置function random(min, max) {const num = Math.floor(Math.random() * (max - min)) + min;return num;}// 用于绘制蛇或者是食物代表的方块,seat为方块位置,取值为0~399,color为颜色function draw(seat, color) {ctx.fillStyle = color; // 填充颜色// fillRect的四个参数分别表示要绘制方块的x坐标,y坐标,长,宽,这里为了美观留了1px用于边框ctx.fillRect((seat % 20) * 20 + 1,Math.floor(seat / 20) * 20 + 1,18,18);}// 同步难度等级function syncMode() {var mode_value = "";for (var i = 0; i < mode_item.length; i++) {if (mode_item[i].checked) {mode_value = mode_item[i].value;//原来是mode_item.value}}switch (mode_value) {case "simply":time_internal = simply_mode;break;case "middle":time_internal = middle_mode;break;case "hard":time_internal = hard_mode;break;}}// 用于绑定键盘上下左右事件,我设置了wsad,或者ijkl,或者上下左右方向键,代表上下左右方向// 同时绑定R 重启,P 暂停,C 继续,注意:若是这几个键则不需要更新direction的值,操作结束后直接返回即可document.onkeydown = function (event) {const keycode = event.keyCode;if(keycode == 82){// R 重启restart_btn.onclick();return;} else if(keycode == 80){// P 暂停pause_btn.onclick();return;} else if(keycode == 67){// C 继续start_btn.onclick();return;} else if (keycode <= 40) {// 上 38 下 40 左 37 右 39n = [-1, -20, 1, 20][keycode - 37] || direction; // 若keycode为其他值,则方向不变} else if (keycode = 73) {// i 73 j 74 k 75 l 76n = [-20, -1, 20, 1][keycode - 73] || direction;} else {switch (keycode) {case 87: //wn = -20;break;case 83: //sn = 20;break;case 65: //an = -1;break;case 68: //dn = 1;break;default:n = direction;}}direction = snake[1] - snake[0] == n ? direction : n; // 若方向与原方向相反,则方向不变};// 用于初始化游戏各项参数function init_game() {snake = [41, 40]; direction = 1; food = 42;score = -1; time_internal = simply_mode;enabled = true;score_cal.innerText = "目前得分: 0分"; // 更新得分mode_item[0].checked = true; // 重置难度等级为简单}function game_over(){cancelAnimationFrame(run_id);game_over_score.innerText = "您的最终得分为: " + score + "分";game_over_p.style.display = "block";}// 启动或继续游戏function run_game() {syncMode(); // 同步难度等级n = snake[0] + direction; // 找到新蛇头坐标snake.unshift(n); // 添加新蛇头// 判断蛇头是否撞到自己或者是否超出边界if (snake.indexOf(n, 1) > 0 ||n 399 ||(direction == 1 && n % 20 == 0) ||(direction == -1 && n % 20 == 19)) {game_over();}draw(n, "#1a8dcc"); // 绘制新蛇头为浅蓝色draw(snake[1], "#cececc"); // 将原来的蛇头(浅蓝色)变成蛇身(浅灰色)if (n == food) {score = score + 1;score_cal.innerText = "目前得分: " + score; // 更新得分while (snake.indexOf((food = random(0, 400))) >= 0); // 重新刷新食物,注意食物应不在蛇内部draw(food, "Yellow"); // 绘制食物} else {draw(snake.pop(), "White"); // 将原来的蛇尾绘制成白色}// setTimeout(arguments.callee, time_internal); //之前的方案,无法实现暂停和游戏的继续}// 控制游戏的刷新频率,每隔time_internal时间间隔刷新一次function game_control(){if(enabled){enabled = false;requestAnimationFrame(run_game);setTimeout(() => enabled = true, time_internal);}run_id = requestAnimationFrame(game_control);}// 绑定开始按钮点击事件start_btn.onclick = function () {run_id = requestAnimationFrame(game_control);};// 绑定暂停按钮点击事件pause_btn.onclick = function () {cancelAnimationFrame(run_id);};// 绑定重新开始按钮点击事件restart_btn.onclick = function () {cancelAnimationFrame(run_id);// 将原有的食物和蛇的方块都绘制成白色for(var i = 0; i < snake.length; i++){draw(snake[i], "White");}draw(food, "White");// 初始化游戏各项参数init_game();run_id = requestAnimationFrame(game_control);};// 绑定游戏结束时的取消按钮点击事件cancel_btn.onclick = function () {for(var i = 0; i < snake.length; i++){draw(snake[i], "White");}draw(food, "White");init_game();game_over_p.style.display = "none";}// 绑定游戏结束时的再来一把按钮点击事件once_again_btn.onclick = function () {for(var i = 0; i < snake.length; i++){draw(snake[i], "White");}draw(food, "White");init_game();game_over_p.style.display = "none";run_id = requestAnimationFrame(game_control);}

【相关推荐:javascript学习教程

以上就是实例分享之JavaScript实现贪吃蛇小游戏的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/553804.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月9日 21:18:15
下一篇 2025年11月9日 21:29:31

相关推荐

  • 项目实践:如何结合CSS和JavaScript打造优秀网页的经验总结

    项目实践:如何结合CSS和JavaScript打造优秀网页的经验总结 随着互联网的快速发展,网页设计已经成为了各行各业都离不开的一项技能。优秀的网页设计可以给用户留下深刻的印象,提升用户体验,增加用户的黏性和转化率。而要做出优秀的网页设计,除了对美学的理解和创意的运用外,还需要掌握一些基本的技能,如…

    2025年12月24日
    200
  • 学完HTML和CSS之后我应该做什么?

    网页开发是一段漫长的旅程,但是掌握了HTML和CSS技能意味着你已经赢得了一半的战斗。这两种语言对于学习网页开发技能来说非常重要和基础。现在不可或缺的是下一个问题,学完HTML和CSS之后我该做什么呢? 对这些问题的答案可以分为2-3个部分,你可以继续练习你的HTML和CSS编码,然后了解在学习完H…

    2025年12月24日
    000
  • 聊聊怎么利用CSS实现波浪进度条效果

    本篇文章给大家分享css 高阶技巧,介绍一下如何使用css实现波浪进度条效果,希望对大家有所帮助! 本文是 CSS Houdini 之 CSS Painting API 系列第三篇。 现代 CSS 之高阶图片渐隐消失术现代 CSS 高阶技巧,像 Canvas 一样自由绘图构建样式! 在上两篇中,我们…

    2025年12月24日 好文分享
    200
  • 巧用距离、角度及光影制作炫酷的 3D 文字特效

    如何利用 css 实现3d立体的数字?下面本篇文章就带大家巧用视觉障眼法,构建不一样的 3d 文字特效,希望对大家有所帮助! 最近群里有这样一个有意思的问题,大家在讨论,使用 CSS 3D 能否实现如下所示的效果: 这里的核心难点在于,如何利用 CSS 实现一个立体的数字?CSS 能做到吗? 不是特…

    2025年12月24日 好文分享
    000
  • CSS高阶技巧:实现图片渐隐消的多种方法

    将专注于实现复杂布局,兼容设备差异,制作酷炫动画,制作复杂交互,提升可访问性及构建奇思妙想效果等方面的内容。 在兼顾基础概述的同时,注重对技巧的挖掘,结合实际进行运用,欢迎大家关注。 正文从这里开始。 在过往,我们想要实现一个图片的渐隐消失。最常见的莫过于整体透明度的变化,像是这样: 立即学习“前端…

    2025年12月24日 好文分享
    000
  • css实现登录按钮炫酷效果(附代码实例)

    今天在网上看到一个炫酷的登录按钮效果;初看时感觉好牛掰;但是一点一点的抛开以后发现,并没有那么难;我会将全部代码贴出来;如果有不对的地方,大家指点一哈。 分析 我们抛开before不谈的话;其实原理和就是通过背景大小以及配合位置达到颜色渐变的效果。 text-transform: uppercase…

    2025年12月24日
    000
  • CSS flex布局属性:align-items和align-content的区别

    在用flex布局时,发现有两个属性功能好像有点类似:align-items和align-content,乍看之下,它们都是用于定义flex容器中元素在交叉轴(主轴为flex-deriction定义的方向,默认为row,那么交叉轴跟主轴垂直即为column,反之它们互调,flex基本的概念如下图所示)…

    2025年12月24日 好文分享
    000
  • 手把手教你用 transition 实现短视频 APP的点赞动画

    怎么使用纯 css 实现有趣的点赞动画?下面本篇文章就带大家了解一下巧妙借助 transition实现点赞动画的方法,希望对大家有所帮助! 在各种短视频界面上,我们经常会看到类似这样的点赞动画: 非常的有意思,有意思的交互会让用户更愿意进行互动。 那么,这么有趣的点赞动画,有没有可能使用纯 CSS …

    2025年12月24日 好文分享
    000
  • 巧用CSS实现各种奇形怪状按钮(附代码)

    本篇文章带大家看看怎么使用 CSS 轻松实现高频出现的各类奇形怪状按钮,希望对大家有所帮助! 怎么样使用 CSS 实现一个内切角按钮呢、怎么样实现一个带箭头的按钮呢? 本文基于一些高频出现在设计稿中的,使用 css 实现稍微有点难度和技巧性的按钮,讲解使用 css 如何尽可能的实现它们。【推荐学习:…

    2025年12月24日 好文分享
    000
  • 原来利用纯CSS也能实现文字轮播与图片轮播!

    怎么制作文字轮播与图片轮播?大家第一想到的是不是利用js,其实利用纯css也能实现文字轮播与图片轮播,下面来看看实现方法,希望对大家有所帮助! 今天,分享一个实际业务中能够用得上的动画技巧。【推荐学习:css视频教程】 巧用逐帧动画,配合补间动画实现一个无限循环的轮播效果,像是这样: 立即学习“前端…

    2025年12月24日 好文分享
    000
  • 总结整理:需要避坑的五大常见css错误(收藏)

    本篇文章给大家总结5个最常见的css错误,并介绍一下避坑方法,希望对大家有所帮助! 正如我们今天所知,CSS语言是web的一个重要组成部分。它使我们有能力绘制元素在屏幕、网页或其他媒体中的展示方式。 它简单、强大,而且是声明式的。我们可以很容易地实现复杂的事情,如暗黑/光明模式。然而,对它有很多误解…

    2025年12月24日
    000
  • CSS+JS实现爱心点赞按钮(代码示例)

    本篇文章给大家介绍一下css+js实现一个“爱之满满”点赞按钮的方法,希望对大家有所帮助! 前段时间在看一档说唱节目,被里面的一个说唱歌手JBcob的爱之满满这句词给洗脑了。 于是这次给大家带来一个爱之满满的点赞按钮,让大家在点赞的同时还能感受到被爱包裹的感觉。 立即学习“前端免费学习笔记(深入)”…

    2025年12月24日 好文分享
    000
  • 让人眼前一亮的五个前端小技巧

    为了让大家编程更轻松一些,本挑选一些有用的但相对比较少见有用的技巧。废话不多说,开车了。 1.快速隐藏 要隐藏一个DOM元素,不需要JavaScript。一个原生的HTML属性就足以隐藏。其效果类似于添加一个style display: none;。 该段落在页面上是不可见的,它对HTML是隐藏的。…

    2025年12月24日
    000
  • 如何实现炫酷的数字大屏

    依托强大无远开发平台,可以快速实现带各种酷炫联动效果的数字化大屏。一起来看一下吧 DEMO 地址:https://previewer.wuyuan.io/p… 配置地址:https://workbench.wuyuan.io/p… 效果图 1 效果图 2 实现步骤 1. 完成…

    2025年12月24日 好文分享
    000
  • 使用JS或CSS如何实现瀑布流布局,几种方案介绍

    本篇文章带大家了解一下瀑布流布局,介绍一下三种靠谱js方案,以及n种不靠谱css方案。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。 本着实用精神,我们今天来分享一下瀑布流布局(昨天有个小兄弟问我怎么做,我找了半天没找到,啊原来写在内网了)。 演示地址: http://www.li…

    2025年12月24日 好文分享
    000
  • 详解CSS3+SVG滤镜实现不规则边框的方法

    本篇文章将介绍一种配合 svg 滤镜实现各种不规则图形添加边框的小技巧。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。 需求背景,给不规则图形添加边框 在我们日常开发中,时长会遇到一些非矩形、非圆形的图案。类似下面这些: 使用纯 CSS,搭配一些技巧,是可以制作出上面的图形的,当然…

    2025年12月24日 好文分享
    000
  • 5个有用的css函数(分享)

    CSS 包含了许多函数,而且它能够完成许多早期需要用 JavaScript才能完成的事情。每年都有新的特性被添加进来,这让我们的开发更加轻松,也减少了对JavaScript的依赖。CSS 函数是它所具有的最强大的特性之一,在本文中,我将介绍一些我认为有用的函数。 (学习视频分享:css视频教程) a…

    2025年12月24日 好文分享
    000
  • CSS元素选择器的运作原理介绍

    推荐教程:CSS视频教程 在前端工程师的日常工作中,使用 CSS 元素选择器是稀松平常的事;无论你是编写一般的 CSS 还是需要经过编译的 SASS,SCSS,LESS等,最终都被编译成一行一行的 CSS 样式属性,最终交给浏览器解析并套用。但是你想过没有这是如何实现的呢? 浏览器渲染 我们先看一下…

    2025年12月24日 好文分享
    000
  • 清除浮动和闭合浮动的介绍

    本篇文章给大家带来的内容是关于清除浮动和闭合浮动的介绍,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 为什么要清除浮动 子元素 float:left; 脱离文档流,会造成父元素塌陷(撑不起来) 父元素 靠子元素撑起来 清除浮动  clear:both 清除浮动 // 伪元素 :af…

    好文分享 2025年12月24日
    000
  • 选择器(picker)插件的实现方法介绍(代码)

    本篇文章给大家带来的内容是关于选择器(picker)插件的实现方法介绍(代码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 一个正常的选择器插件是非常细致的,一步一步来描述就是。手指滑动内容跟随手指滚动,当内容到底或触顶的时候就不能在滚动并且内容要一直保持在正确的位置上。 第一步…

    2025年12月24日
    000

发表回复

登录后才能评论
关注微信