好玩的小游戏系列 (一)基于html+js 原生贪吃蛇

简介: 好玩的小游戏系列 (一)基于html+js 原生贪吃蛇

一朵花如果只被用来观赏那只呈现出它的外在意义只是它生命的一部分若是不能够将其内在更实质的美发挥出来充其量也不过就是一朵死的花而已。

目录

一、前言

二、代码介绍

三、效果显示

四、编码实现

index.html

jquery-1.10.2.js

五、获取源码

       获取源码?私信?关注?点赞?收藏?

一、前言

贪吃蛇是一款经典的小游戏。初始是像素版本,后来又衍生出3D版本、多人对战版本等。

贪食蛇游戏操作简单,可玩性比较高。这个游戏难度最大的不是蛇长得很长的时候,而是开始。那个时候蛇身很短,看上去难度不大,却最容易死掉,因为把玩一条小短蛇让人容易走神,失去耐心。由于难度小,你会不知不觉加快调整方向的速度,在游走自如的时候蛇身逐渐加长了,而玩家却没有意识到危险。

贪食蛇的另一个危险期在于游戏开始几十秒之后。由于玩家的注意力高度集中,精神紧张,此时局面稍好,就会不由自主地想放松一下,结果手指一松劲,贪食蛇就死了。所以贪食蛇可以算作一个敏捷型的小游戏。

二、代码介绍

一款简单的 HTML+JS 原生贪吃蛇小游戏

1、HTML

2、JS

3、舒适的画面感

4、流畅的游戏体验

三、效果显示  

我们一起回味一下好玩的贪吃蛇吧!!!

A.

B.

舒适美观的画面,灵动的体验!!!

是否想体验一下呢?

四、编码实现

由于文章篇幅限制,部分代码将不予展示,但会将完整代码文件放在下方

注意路径(⊙o⊙)?

o(* ̄▽ ̄*)ブ

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>JS贪吃蛇</title>
    <script src="static/js/jquery-1.10.2.js"></script>
    <style>
      @font-face {
        font-family: "game";
        src: url("https://fonts.googleapis.com/css2?family=Poppins:wght@500;800&display=swap");
      }
      * {
        padding: 0;
        margin: 0;
        box-sizing: border-box;
      }
      button:focus {
        outline: 0;
      }
      html,
      body {
        height: 100%;
        font-family: "Poppins", sans-serif;
        color: #6e7888;
      }
      body {
        background-color: #222738;
        display: flex;
        justify-content: center;
        align-items: center;
        color: #6e7888;
      }
      canvas {
        background-color: #181825;
      }
      .container {
        display: flex;
        width: 100%;
        height: 100%;
        flex-flow: column wrap;
        justify-content: center;
        align-items: center;
      }
      #ui {
        display: flex;
        align-items: center;
        font-size: 10px;
        flex-flow: column;
        margin-left: 10px;
      }
      h2 {
        font-weight: 200;
        transform: rotate(270deg);
      }
      #score {
        margin-top: 20px;
        font-size: 30px;
        font-weight: 800;
      }
      .noselect {
        user-select: none;
      }
      #replay {
        font-size: 10px;
        padding: 10px 20px;
        background: #6e7888;
        border: none;
        color: #222738;
        border-radius: 20px;
        font-weight: 800;
        transform: rotate(270deg);
        cursor: pointer;
        transition: all 200ms cubic-bezier(0.4, 0, 0.2, 1);
      }
      #replay:hover {
        background: #a6aab5;
        background: #4cffd7;
        transition: all 200ms cubic-bezier(0.4, 0, 0.2, 1);
      }
      #replay svg {
        margin-right: 8px;
      }
      @media (max-width: 600px) {
        #replay {
          margin-bottom: 20px;
        }
        #replay,
        h2 {
          transform: rotate(0deg);
        }
        #ui {
          flex-flow: row wrap;
          margin-bottom: 20px;
        }
        #score {
          margin-top: 0;
          margin-left: 20px;
        }
        .container {
          flex-flow: column wrap;
        }
      }
      #author {
        width: 100%;
        bottom: 40px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        font-weight: 600;
        color: inherit;
        text-transform: uppercase;
        padding-left: 35px;
      }
      #author span {
        font-size: 10px;
        margin-left: 20px;
        color: inherit;
        letter-spacing: 4px;
      }
      #author h1 {
        font-size: 25px;
      }
      .wrapper {
        display: flex;
        flex-flow: row wrap;
        justify-content: center;
        align-items: center;
        margin-bottom: 20px;
      }
    </style>
  </head>
  <body>
    <div class="container noselect">
      <div class="wrapper">
        <button id="replay">
          <i class="fas fa-play"></i>
          RESTART
        </button>
        <div id="canvas"></div>
        <div id="ui">
          <h2>SCORE</h2>
          <span id="score">00</span>
        </div>
      </div>
      <div id="author">
        <h1>SNAKE</h1>
        <span><p>小键盘方向键控制</p></span>
      </div>
    </div>
    <script>
      /** 
This is a snake game I made with Vanilla Javascript.
Follow me on twitter @fariatondo
**/
      let dom_replay = document.querySelector("#replay");
      let dom_score = document.querySelector("#score");
      let dom_canvas = document.createElement("canvas");
      document.querySelector("#canvas").appendChild(dom_canvas);
      let CTX = dom_canvas.getContext("2d");
      const W = (dom_canvas.width = 400);
      const H = (dom_canvas.height = 400);
      let snake,
        food,
        currentHue,
        cells = 20,
        cellSize,
        isGameOver = false,
        tails = [],
        score = 00,
        maxScore = window.localStorage.getItem("maxScore") || undefined,
        particles = [],
        splashingParticleCount = 20,
        cellsCount,
        requestID;
      let helpers = {
        Vec: class {
          constructor(x, y) {
            this.x = x;
            this.y = y;
          }
          add(v) {
            this.x += v.x;
            this.y += v.y;
            return this;
          }
          mult(v) {
            if (v instanceof helpers.Vec) {
              this.x *= v.x;
              this.y *= v.y;
              return this;
            } else {
              this.x *= v;
              this.y *= v;
              return this;
            }
          }
        },
        isCollision(v1, v2) {
          return v1.x == v2.x && v1.y == v2.y;
        },
        garbageCollector() {
          for (let i = 0; i < particles.length; i++) {
            if (particles[i].size <= 0) {
              particles.splice(i, 1);
            }
          }
        },
        drawGrid() {
          CTX.lineWidth = 1.1;
          CTX.strokeStyle = "#232332";
          CTX.shadowBlur = 0;
          for (let i = 1; i < cells; i++) {
            let f = (W / cells) * i;
            CTX.beginPath();
            CTX.moveTo(f, 0);
            CTX.lineTo(f, H);
            CTX.stroke();
            CTX.beginPath();
            CTX.moveTo(0, f);
            CTX.lineTo(W, f);
            CTX.stroke();
            CTX.closePath();
          }
        },
        randHue() {
          return ~~(Math.random() * 360);
        },
        hsl2rgb(hue, saturation, lightness) {
          if (hue == undefined) {
            return [0, 0, 0];
          }
          var chroma = (1 - Math.abs(2 * lightness - 1)) * saturation;
          var huePrime = hue / 60;
          var secondComponent = chroma * (1 - Math.abs((huePrime % 2) - 1));
          huePrime = ~~huePrime;
          var red;
          var green;
          var blue;
          if (huePrime === 0) {
            red = chroma;
            green = secondComponent;
            blue = 0;
          } else if (huePrime === 1) {
            red = secondComponent;
            green = chroma;
            blue = 0;
          } else if (huePrime === 2) {
            red = 0;
            green = chroma;
            blue = secondComponent;
          } else if (huePrime === 3) {
            red = 0;
            green = secondComponent;
            blue = chroma;
          } else if (huePrime === 4) {
            red = secondComponent;
            green = 0;
            blue = chroma;
          } else if (huePrime === 5) {
            red = chroma;
            green = 0;
            blue = secondComponent;
          }
          var lightnessAdjustment = lightness - chroma / 2;
          red += lightnessAdjustment;
          green += lightnessAdjustment;
          blue += lightnessAdjustment;
          return [
            Math.round(red * 255),
            Math.round(green * 255),
            Math.round(blue * 255),
          ];
        },
        lerp(start, end, t) {
          return start * (1 - t) + end * t;
        },
      };
      let KEY = {
        ArrowUp: false,
        ArrowRight: false,
        ArrowDown: false,
        ArrowLeft: false,
        resetState() {
          this.ArrowUp = false;
          this.ArrowRight = false;
          this.ArrowDown = false;
          this.ArrowLeft = false;
        },
        listen() {
          addEventListener(
            "keydown",
            (e) => {
              if (e.key === "ArrowUp" && this.ArrowDown) return;
              if (e.key === "ArrowDown" && this.ArrowUp) return;
              if (e.key === "ArrowLeft" && this.ArrowRight) return;
              if (e.key === "ArrowRight" && this.ArrowLeft) return;
              this[e.key] = true;
              Object.keys(this)
                .filter(
                  (f) => f !== e.key && f !== "listen" && f !== "resetState"
                )
                .forEach((k) => {
                  this[k] = false;
                });
            },
            false
          );
        },
      };
      class Snake {
        constructor(i, type) {
          this.pos = new helpers.Vec(W / 2, H / 2);
          this.dir = new helpers.Vec(0, 0);
          this.type = type;
          this.index = i;
          this.delay = 5;
          this.size = W / cells;
          this.color = "white";
          this.history = [];
          this.total = 1;
        }
        draw() {
          let { x, y } = this.pos;
          CTX.fillStyle = this.color;
          CTX.shadowBlur = 20;
          CTX.shadowColor = "rgba(255,255,255,.3 )";
          CTX.fillRect(x, y, this.size, this.size);
          CTX.shadowBlur = 0;
          if (this.total >= 2) {
            for (let i = 0; i < this.history.length - 1; i++) {
              let { x, y } = this.history[i];
              CTX.lineWidth = 1;
              CTX.fillStyle = "rgba(225,225,225,1)";
              CTX.fillRect(x, y, this.size, this.size);
            }
          }
        }
        walls() {
          let { x, y } = this.pos;
          if (x + cellSize > W) {
            this.pos.x = 0;
          }
          if (y + cellSize > W) {
            this.pos.y = 0;
          }
          if (y < 0) {
            this.pos.y = H - cellSize;
          }
          if (x < 0) {
            this.pos.x = W - cellSize;
          }
        }
        controlls() {
          let dir = this.size;
          if (KEY.ArrowUp) {
            this.dir = new helpers.Vec(0, -dir);
          }
          if (KEY.ArrowDown) {
            this.dir = new helpers.Vec(0, dir);
          }
          if (KEY.ArrowLeft) {
            this.dir = new helpers.Vec(-dir, 0);
          }
          if (KEY.ArrowRight) {
            this.dir = new helpers.Vec(dir, 0);
          }
        }
        selfCollision() {
          for (let i = 0; i < this.history.length; i++) {
            let p = this.history[i];
            if (helpers.isCollision(this.pos, p)) {
              isGameOver = true;
            }
          }
        }
        update() {
          this.walls();
          this.draw();
          this.controlls();
          if (!this.delay--) {
            if (helpers.isCollision(this.pos, food.pos)) {
              incrementScore();
              particleSplash();
              food.spawn();
              this.total++;
            }
            this.history[this.total - 1] = new helpers.Vec(
              this.pos.x,
              this.pos.y
            );
            for (let i = 0; i < this.total - 1; i++) {
              this.history[i] = this.history[i + 1];
            }
            this.pos.add(this.dir);
            this.delay = 5;
            this.total > 3 ? this.selfCollision() : null;
          }
        }
      }
      class Food {
        constructor() {
          this.pos = new helpers.Vec(
            ~~(Math.random() * cells) * cellSize,
            ~~(Math.random() * cells) * cellSize
          );
          this.color = currentHue = `hsl(${~~(Math.random() * 360)},100%,50%)`;
          this.size = cellSize;
        }
        draw() {
          let { x, y } = this.pos;
          CTX.globalCompositeOperation = "lighter";
          CTX.shadowBlur = 20;
          CTX.shadowColor = this.color;
          CTX.fillStyle = this.color;
          CTX.fillRect(x, y, this.size, this.size);
          CTX.globalCompositeOperation = "source-over";
          CTX.shadowBlur = 0;
        }
        spawn() {
          let randX = ~~(Math.random() * cells) * this.size;
          let randY = ~~(Math.random() * cells) * this.size;
          for (let path of snake.history) {
            if (helpers.isCollision(new helpers.Vec(randX, randY), path)) {
              return this.spawn();
            }
          }
          this.color = currentHue = `hsl(${helpers.randHue()}, 100%, 50%)`;
          this.pos = new helpers.Vec(randX, randY);
        }
      }
      class Particle {
        constructor(pos, color, size, vel) {
          this.pos = pos;
          this.color = color;
          this.size = Math.abs(size / 2);
          this.ttl = 0;
          this.gravity = -0.2;
          this.vel = vel;
        }
        draw() {
          let { x, y } = this.pos;
          let hsl = this.color
            .split("")
            .filter((l) => l.match(/[^hsl()$% ]/g))
            .join("")
            .split(",")
            .map((n) => +n);
          let [r, g, b] = helpers.hsl2rgb(hsl[0], hsl[1] / 100, hsl[2] / 100);
          CTX.shadowColor = `rgb(${r},${g},${b},${1})`;
          CTX.shadowBlur = 0;
          CTX.globalCompositeOperation = "lighter";
          CTX.fillStyle = `rgb(${r},${g},${b},${1})`;
          CTX.fillRect(x, y, this.size, this.size);
          CTX.globalCompositeOperation = "source-over";
        }
        update() {
          this.draw();
          this.size -= 0.3;
          this.ttl += 1;
          this.pos.add(this.vel);
          this.vel.y -= this.gravity;
        }
      }
      function incrementScore() {
        score++;
        dom_score.innerText = score.toString().padStart(2, "0");
      }
      function particleSplash() {
        for (let i = 0; i < splashingParticleCount; i++) {
          let vel = new helpers.Vec(
            Math.random() * 6 - 3,
            Math.random() * 6 - 3
          );
          let position = new helpers.Vec(food.pos.x, food.pos.y);
          particles.push(new Particle(position, currentHue, food.size, vel));
        }
      }
      function clear() {
        CTX.clearRect(0, 0, W, H);
      }
      function initialize() {
        CTX.imageSmoothingEnabled = false;
        KEY.listen();
        cellsCount = cells * cells;
        cellSize = W / cells;
        snake = new Snake();
        food = new Food();
        dom_replay.addEventListener("click", reset, false);
        loop();
      }
      function loop() {
        clear();
        if (!isGameOver) {
          requestID = setTimeout(loop, 1000 / 60);
          helpers.drawGrid();
          snake.update();
          food.draw();
          for (let p of particles) {
            p.update();
          }
          helpers.garbageCollector();
        } else {
          clear();
          gameOver();
        }
      }
      function gameOver() {
        maxScore ? null : (maxScore = score);
        score > maxScore ? (maxScore = score) : null;
        window.localStorage.setItem("maxScore", maxScore);
        CTX.fillStyle = "#4cffd7";
        CTX.textAlign = "center";
        CTX.font = "bold 30px Poppins, sans-serif";
        CTX.fillText("GAME OVER", W / 2, H / 2);
        CTX.font = "15px Poppins, sans-serif";
        CTX.fillText(`SCORE   ${score}`, W / 2, H / 2 + 60);
        CTX.fillText(`MAXSCORE   ${maxScore}`, W / 2, H / 2 + 80);
      }
      function reset() {
        dom_score.innerText = "00";
        score = "00";
        snake = new Snake();
        food.spawn();
        KEY.resetState();
        isGameOver = false;
        clearTimeout(requestID);
        loop();
      }
      initialize();
    </script>
    <script>
      console.log("微信公众号搜索 Enovo开发工厂");
      console.log("CSDN搜索 Enovo_飞鱼");
    </script>
  </body>
</html>

jquery-1.10.2.js

document.write("<script src='https://s1.pstatp.com/cdn/expire-1-M/jquery/1.10.2/jquery.min.js'><\/script>");

五、获取源码

老规矩,先给朋友们看一下完整文件夹

正确的文件如下图

第一步,通过微信公众号下载源码压缩包,解压并打开文件夹,即为上图样式(复制源码请注意路径及文件名)

第二步,点击 html 文件打开即可

作为新年第三辑,希望得到大家的喜欢🙇‍

新的一年,又是一个崭新的开始,充满信心,充满希望,充满阳光,走向明天,走向理想!

在后面的日子里,我也会继续更新一些简单的小游戏内容

不仅仅包括的是 HTML ,会增加 C语言 、Python  等等

如果大家有好的意见或者建议

可以提出来

🙇‍

以上就是本篇文章的全部内容了

获取源码?私信?关注?点赞?收藏?

 👍+✏️+⭐️+🙇‍

相关文章
|
18天前
|
JSON JavaScript 前端开发
JavaScript原生代码处理JSON的一些高频次方法合集
JavaScript原生代码处理JSON的一些高频次方法合集
|
18天前
|
JavaScript 算法
原生JS完成“一对一、一对多”矩形DIV碰撞检测、碰撞检查,通过计算接触面积(重叠覆盖面积)大小来判断接触对象DOM
原生JS完成“一对一、一对多”矩形DIV碰撞检测、碰撞检查,通过计算接触面积(重叠覆盖面积)大小来判断接触对象DOM
|
23小时前
|
JavaScript 前端开发 BI
原生html—摆脱ps、excel 在线绘制财务表格加水印(html绘制表格js加水印)
原生html—摆脱ps、excel 在线绘制财务表格加水印(html绘制表格js加水印)
6 1
|
4天前
|
JavaScript 前端开发 UED
深入解析JavaScript原生操作DOM技术
【4月更文挑战第22天】本文深入探讨JavaScript原生DOM操作技术,包括使用`getElement*`方法和CSS选择器获取元素,借助`createElement`与`appendChild`动态创建及插入元素,修改元素内容、属性和样式,以及删除元素。通过掌握这些技术,开发者能实现页面动态交互,但应注意避免过度操作DOM以优化性能和用户体验。
|
9天前
|
JavaScript 前端开发
js怎么删除html元素
js怎么删除html元素
23 10
|
17天前
|
JSON JavaScript 前端开发
js是什么、html、css
js是什么、html、css
|
18天前
|
JavaScript
【归总】原生js操作浏览器hash、url参数参数获取/修改方法合集
【归总】原生js操作浏览器hash、url参数参数获取/修改方法合集
|
29天前
|
JavaScript 前端开发 算法
游戏物理系统 - 介绍一下Box2D或其他物理引擎在JS小游戏中的使用。
Box2D, a popular 2D physics engine, simulates rigid body dynamics, collision detection, and constraints for JavaScript games via WebAssembly. It offers realistic physics, efficient collision handling, and customizable APIs.
16 4
|
1月前
|
JavaScript 计算机视觉
纯js实现人脸识别眨眨眼张张嘴案例——index.html
纯js实现人脸识别眨眨眼张张嘴案例——index.html
17 0
|
1月前
|
前端开发 JavaScript
从0到1:用HTML、CSS和JavaScript构建一个简单的待办事项列表
从0到1:用HTML、CSS和JavaScript构建一个简单的待办事项列表
26 0