一朵花如果只被用来观赏那只呈现出它的外在意义只是它生命的一部分若是不能够将其内在更实质的美发挥出来充其量也不过就是一朵死的花而已。
目录
一、前言
贪吃蛇是一款经典的小游戏。初始是像素版本,后来又衍生出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 等等
如果大家有好的意见或者建议
可以提出来
🙇
以上就是本篇文章的全部内容了
获取源码?私信?关注?点赞?收藏?
👍+✏️+⭐️+🙇