贪食蛇游戏很有趣, 而且也不复杂, 是学习一门编程语言的最好的入门练手的项目. 我在加入GE后接触到GE开发的Magik语言(Wiki), 便很快的用它写了一个贪吃蛇的游戏.
今天, 我们就来用当今最火的语言 Javascript 来写一下, 你就会发现, 其实这个游戏很简单就能实现了.
画板
我们需要一个游戏场景, 也就是画板 Canvas, 画板上我们需要每次清空, 然后画上蛇和苹果.
1 | <canvas width="400" height="400" id="game"></canvas> |
<canvas width="400" height="400" id="game"></canvas>
然后, 我们需要定义几个全局变量:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | var context; var canvas; // 画板 var score = 0; // 分数 var bestscore = 0; // 最高分数 var grid = 16; // 每格相素点 var count = 0; var snake = { x: 160, y: 160, // 方向偏移量 dx: grid, dy: 0, // 蛇的身体坐标 cells: [], // 蛇的最小长度 maxCells: 4 }; var apple = { x: 320, y: 320 }; |
var context; var canvas; // 画板 var score = 0; // 分数 var bestscore = 0; // 最高分数 var grid = 16; // 每格相素点 var count = 0; var snake = { x: 160, y: 160, // 方向偏移量 dx: grid, dy: 0, // 蛇的身体坐标 cells: [], // 蛇的最小长度 maxCells: 4 }; var apple = { x: 320, y: 320 };
游戏控制
在HTML页面加载完后, 我们可以通过事件 body.onload 来加载一个 windowload 函数.
1 | <body onload="windowload()"> |
<body onload="windowload()">
在这个函数里, 我们需要定义对蛇的控制, 也就是四个方向键. 当蛇往右的行走的时候, 方向左右键是没有效果的, 同样的, 当蛇往上行走的时候, 方向上下键是没有效果的, 这样避免了贪吃蛇马上吃到自己就狗带了.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | function windowload() { canvas = document.getElementById('game'); canvas.setAttribute('tabindex','0'); canvas.focus(); context = canvas.getContext('2d'); // 按键事件 document.addEventListener('keydown', function(e) { // 左 if (e.which === 37 && snake.dx === 0) { snake.dx = -grid; snake.dy = 0; } // 上 else if (e.which === 38 && snake.dy === 0) { snake.dy = -grid; snake.dx = 0; } // 右 else if (e.which === 39 && snake.dx === 0) { snake.dx = grid; snake.dy = 0; } // 下 else if (e.which === 40 && snake.dy === 0) { snake.dy = grid; snake.dx = 0; } }); window.requestAnimationFrame(loop); } |
function windowload() { canvas = document.getElementById('game'); canvas.setAttribute('tabindex','0'); canvas.focus(); context = canvas.getContext('2d'); // 按键事件 document.addEventListener('keydown', function(e) { // 左 if (e.which === 37 && snake.dx === 0) { snake.dx = -grid; snake.dy = 0; } // 上 else if (e.which === 38 && snake.dy === 0) { snake.dy = -grid; snake.dx = 0; } // 右 else if (e.which === 39 && snake.dx === 0) { snake.dx = grid; snake.dy = 0; } // 下 else if (e.which === 40 && snake.dy === 0) { snake.dy = grid; snake.dx = 0; } }); window.requestAnimationFrame(loop); }
在函数的最后我们调用了 window.requestAnimationFrame函数. 这个函数需要一个回调函数. 让画板在重画的时候会调用它.
我们需要几个用到的 helper 函数.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | // 返回一个在 [min, max) 的随机整数 function getRandomInt(min, max) { return Math.floor(Math.random() * (max - min)) + min; } function showScore(score) { document.getElementById('score').innerHTML = score; } function showBestScore(score) { document.getElementById('bestscore').innerHTML = score; } // 重置游戏 function resetGame() { snake.x = 160; snake.y = 160; snake.cells = []; snake.maxCells = 4; snake.dx = grid; snake.dy = 0; score = 0; showScore(score); apple.x = getRandomInt(0, 25) * grid; apple.y = getRandomInt(0, 25) * grid; } |
// 返回一个在 [min, max) 的随机整数 function getRandomInt(min, max) { return Math.floor(Math.random() * (max - min)) + min; } function showScore(score) { document.getElementById('score').innerHTML = score; } function showBestScore(score) { document.getElementById('bestscore').innerHTML = score; } // 重置游戏 function resetGame() { snake.x = 160; snake.y = 160; snake.cells = []; snake.maxCells = 4; snake.dx = grid; snake.dy = 0; score = 0; showScore(score); apple.x = getRandomInt(0, 25) * grid; apple.y = getRandomInt(0, 25) * grid; }
主游戏循环
在游戏循环中, 我们需要递归地调用 requestAnimationFrame. 然后, 清除画布并绘制蛇的身体碎片和苹果. 如果蛇撞到墙壁或与身体碰撞, 则需要触发游戏结束. 当它移动时, 我们可以从它的尾部弹出一个坐标并将其插入到数组前面(头部).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | function loop() { requestAnimationFrame(loop); // 缓慢游戏循环速度到15 fps = 60/4 if (++count < 4) { return; } count = 0; context.clearRect(0,0,canvas.width,canvas.height); // 蛇的位置移到下一格 snake.x += snake.dx; snake.y += snake.dy; if ((snake.x < 0) || (snake.x >= canvas.width)) { resetGame(); return; } if ((snake.y < 0) || (snake.y >= canvas.height)) { resetGame(); return; } // 把新位置加到头部 snake.cells.unshift({x: snake.x, y: snake.y}); // 如果大于 maxCells, 我们就把尾巴去掉一个 if (snake.cells.length > snake.maxCells) { snake.cells.pop(); } // 画苹果 context.fillStyle = 'red'; context.fillRect(apple.x, apple.y, grid-1, grid-1); // 每次画一个绿色的身体 context.fillStyle = 'green'; snake.cells.forEach(function(cell, index) { // 绘制比网格小1像素的像素会在蛇体内创建网格效果, 因此您可以看到它有多长 context.fillRect(cell.x, cell.y, grid-1, grid-1); // 吃了苹果 if (cell.x === apple.x && cell.y === apple.y) { snake.maxCells++; // 画板 400x400 也就是 25x25 格 apple.x = getRandomInt(0, 25) * grid; apple.y = getRandomInt(0, 25) * grid; score ++; bestscore = Math.max(bestscore, score); showBestScore(bestscore); showScore(score); } // 是否和身体碰撞了 for (var i = index + 1; i < snake.cells.length; i += 1) { // snake occupies same space as a body part. reset game if (cell.x === snake.cells[i].x && cell.y === snake.cells[i].y) { resetGame(); return; } } }); } |
function loop() { requestAnimationFrame(loop); // 缓慢游戏循环速度到15 fps = 60/4 if (++count < 4) { return; } count = 0; context.clearRect(0,0,canvas.width,canvas.height); // 蛇的位置移到下一格 snake.x += snake.dx; snake.y += snake.dy; if ((snake.x < 0) || (snake.x >= canvas.width)) { resetGame(); return; } if ((snake.y < 0) || (snake.y >= canvas.height)) { resetGame(); return; } // 把新位置加到头部 snake.cells.unshift({x: snake.x, y: snake.y}); // 如果大于 maxCells, 我们就把尾巴去掉一个 if (snake.cells.length > snake.maxCells) { snake.cells.pop(); } // 画苹果 context.fillStyle = 'red'; context.fillRect(apple.x, apple.y, grid-1, grid-1); // 每次画一个绿色的身体 context.fillStyle = 'green'; snake.cells.forEach(function(cell, index) { // 绘制比网格小1像素的像素会在蛇体内创建网格效果, 因此您可以看到它有多长 context.fillRect(cell.x, cell.y, grid-1, grid-1); // 吃了苹果 if (cell.x === apple.x && cell.y === apple.y) { snake.maxCells++; // 画板 400x400 也就是 25x25 格 apple.x = getRandomInt(0, 25) * grid; apple.y = getRandomInt(0, 25) * grid; score ++; bestscore = Math.max(bestscore, score); showBestScore(bestscore); showScore(score); } // 是否和身体碰撞了 for (var i = index + 1; i < snake.cells.length; i += 1) { // snake occupies same space as a body part. reset game if (cell.x === snake.cells[i].x && cell.y === snake.cells[i].y) { resetGame(); return; } } }); }
如果蛇可以从屏幕的一边穿越到另一边, 你则需要使用下面的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // 水平穿越 if (snake.x < 0) { snake.x = canvas.width - grid; } else if (snake.x >= canvas.width) { snake.x = 0; } // 垂直穿越 if (snake.y < 0) { snake.y = canvas.height - grid; } else if (snake.y >= canvas.height) { snake.y = 0; } |
// 水平穿越 if (snake.x < 0) { snake.x = canvas.width - grid; } else if (snake.x >= canvas.width) { snake.x = 0; } // 垂直穿越 if (snake.y < 0) { snake.y = canvas.height - grid; } else if (snake.y >= canvas.height) { snake.y = 0; }
在线玩贪吃蛇游戏: https://helloacm.com/static/game/snake/
想立马玩贪吃蛇游戏?
英文: How to Make a Simple Snake Game in Javascript?
强烈推荐
- 英国代购-畅购英伦
- TopCashBack 返现 (英国购物必备, 积少成多, 我2年来一共得了3000多英镑)
- Quidco 返现 (也是很不错的英国返现网站, 返现率高)
- 注册就送10美元, 免费使用2个月的 DigitalOcean 云主机(性价比超高, 每月只需5美元)
- 注册就送10美元, 免费使用4个月的 Vultr 云主机(性价比超高, 每月只需2.5美元)
- 注册就送10美元, 免费使用2个月的 阿里 云主机(性价比超高, 每月只需4.5美元)
- 注册就送20美元, 免费使用4个月的 Linode 云主机(性价比超高, 每月只需5美元) (折扣码: PodCastInit2022)
- PlusNet 英国光纤(超快, 超划算! 用户名 doctorlai)
- 刷了美国运通信用卡一年得到的积分 换了 485英镑
- 注册就送50英镑 – 英国最便宜最划算的电气提供商
- 能把比特币莱特币变现的银行卡! 不需要手续费就可以把虚拟货币法币兑换
微信公众号: 小赖子的英国生活和资讯 JustYYUK