ホームページ  >  記事  >  ウェブフロントエンド  >  スネーク ゲームの JavaScript 実装の共有例

スネーク ゲームの JavaScript 実装の共有例

WBOY
WBOY転載
2022-01-05 17:23:432022ブラウズ

この記事では、JavaScript を使用して Snake ゲームを実装する例を紹介します。

スネーク ゲームの JavaScript 実装の共有例

#JavaScript はスネーク ゲームを実現します

機能概要

このプログラムは次の機能を実装します。

    #Snake の基本機能
  1. #統計スコア
  2. #開始と一時停止

  3. 難易度の選択

  4. ショートカット キーの設定

  5. 5.1 ijkl を通じて、wsad は方向切り替えも実現できます
  6. 5.2 「P」は一時停止を意味します。 「C」は開始または続行を意味し、「R」は再起動を意味します

    実装プロセス

最初の実装原則は実際に基づいていますcsdn の巨匠のリファレンスです。彼は 20 行の JavaScript で Snake の基本機能を実装しました。賞賛に値するのは、バグがないことです。リンクはここですSnake を実装するには、大まかにそこにあります手順は次のとおりです。

#ヘビの動く領域を描く

  • ヘビを描く

  • drawfood

  • ヘビを動かしてみましょう

  • ゲームのルールを設定します

  • 難易度を設定します

  • 開始と一時停止の設定

  • ゲーム終了後のその後の動作の設定

  • 人間とコンピュータのインタラクションの実現ページ

  • 注: 次のプロセスの説明部分では、原理と実装の一部のみを説明しています。理解しやすくするために、以下の説明を読みながら最終的な完全なコードを読むことをお勧めします。原理と実装

ヘビのアクティブ領域の描画

まず、ヘビのアクティブ領域を描画します。 JavaScript キャンバスを使用して描画します ヘビの活動領域として 400×400 400\times 400400×400 の領域を使用します

<canvas></canvas>

同時に、CSS を通じて境界線を設定します

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

ヘビと食べ物を描きます

効果は次のとおりです:

ヘビを描く前に、次のことを行う必要があります。ヘビのデータ構造について考えてください。ここでは、ヘビを表すために最も単純なキューを使用します

スネーク ゲームの JavaScript 実装の共有例

チームの先頭はヘビの頭の位置を表し、チームの尾はチームの尾の位置を表しますはヘビの尻尾の位置を表します

  • を描きます ​​ ​​                                       400                                     ×                                     400                                                           400\×400 ​​

    #400##×##400エリアは 400# に分割されます ​​ ​​                                       20                                     ×                                     20                                                           20\×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就可以表示新蛇头的位置,这样我们就可以表示蛇的运动了。

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

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

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

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

    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 = 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; // 若方向与原方向相反,则方向不变};

    设定游戏规则

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

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

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

    注:下面的一段代码中的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; // 更新得分

    设置难度等级

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

    
    
        难度等级:                               

    效果如下:

    スネーク ゲームの JavaScript 実装の共有例

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

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

    我们创建一个时间间隔变量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 <p>最后只需要在蛇每次移动前调用一次上述函数<code>syncMode()</code>就可以实现难度切换,至于蛇的速度的具体调节且看下面如何讲解</p><h4><strong>设置开始与暂停</strong></h4><p>如何实现蛇的移动动态效果,如何暂停,如何继续,速度如何调节,这就涉及到JavaScript的动画的部分了,建议看下《JavaScript高级程序设计(第4版)》第18章的部分,讲的很详细。</p><p>在最初的“20行JavaScript实现贪吃蛇”中并没有实现开始与暂停,其实现动态效果的方法为设置一个立即执行函数<code>!function() {}();</code>,然后在该函数中使用<code>setTimeout(arguments.callee, 150);</code>,每隔0.15秒调用此函数,从而实现了网页的不断刷新,也就是所谓的动态效果。</p><p>后来,我通过web课程老师的案例(弹球游戏)中了解到requestAnimationFrame方法可以实现动画效果,于是我便百度查询,最后在翻书《JavaScript高级程序设计(第4版)》第18章动画与Canvas图形中得到启发–如何实现开始与取消,如何自定义时间间隔(实现难度调节,蛇的速度)</p><p>书中给出的开始动画与取消动画的方法如下:</p><blockquote><p>注:为了便于理解,自己修改过原方法</p></blockquote><pre class="brush:php;toolbar:false">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);就可以了

    设置游戏结束后续操作

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

    效果如下:

    スネーク ゲームの JavaScript 実装の共有例

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

    <p>
        </p><h3>游戏结束!</h3>
        <h3>您的最终得分为: 0分</h3>
        <button>再来一把</button>
        <button>取消</button>

    其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";}

    实现人机交互页面

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

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

    效果如下:

    スネーク ゲームの JavaScript 実装の共有例

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

    我们首先看下绑定按键点击事件,点击”开始/继续“只需要调用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 <p>接下来,我们绑定game over中的两个按键”再来一把“和”取消“</p><p>”再来一把“只需要完成“重新开始”里面的事件即可,”取消“只需要完成”重新开始“点击操作中除了开始游戏的部分,即除了<code>run_id = requestAnimationFrame(game_control);</code></p><p>这两个按钮都需要设置”弹窗“的<code>display</code>属性为<code>none</code></p><p>具体实现如下:</p><pre class="brush:php;toolbar:false">// 绑定游戏结束时的取消按钮点击事件cancel_btn.onclick = function () {
        for(var i = 0; i <p>最后,我们来讲解下如何设置快捷键,快捷键只需要用JavaScript模拟点击对应的按钮即可,实现如下:</p><pre class="brush:php;toolbar:false">// 同时绑定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的操作中只模拟了”开始 / 继续“按钮的点击?

    效果如下:

    スネーク ゲームの JavaScript 実装の共有例

    调试过程:因为蛇头的位置是由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 = 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 

    完整代码

    nbsp;html>
      
        
        
        
        贪吃蛇小游戏
        
      
      
        

    贪吃蛇小游戏

        <canvas></canvas>

    游戏结束!

    您的最终得分为: 0分


    游戏说明:

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

             

    目前得分: 0分

        
          难度等级:                                                
        
                       <script> 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~399 var 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 右 39 n = [-1, -20, 1, 20][keycode - 37] || direction; // 若keycode为其他值,则方向不变 } else if (keycode <= 76 && 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; // 若方向与原方向相反,则方向不变 }; // 用于初始化游戏各项参数 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 < 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); } </script>   

    【相关推荐:javascript学习教程

以上がスネーク ゲームの JavaScript 実装の共有例の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcsdn.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。