想制作自己的经典街机游戏吗?
我一直在观看有关教AI玩蛇的YouTube视频。 不用说,这很难。 同时, 自1976年以来 , Snake游戏就一直是对人类的正确挑战! 从哲学上讲,当我们自己玩游戏时,为什么还要教机器玩游戏呢?
因此,我决定制作Snake并玩它。
“但话又说回来,”我意识到,“当我可以将游戏基于别人的代码时,为什么要从头开始制作Snake?”
我在网上浏览了一个好的模板,然后找到了一个。 现在,我将引导您完成制作Snake的代码,并尽我所能向其解释。
该项目包含以下部分:
- HTML页面
- 游戏画布
- 主循环
- 键盘控制
- 发射
1. HTML页面
该页面很简单:它只是一个空白的HTML模板,带有标题和一个空的正文:
<!DOCTYPE html>
< html >
< head >
< title > The Snake </ title >
< style > </ style >
</ head >
< body >
<!-- Here be the game -->
</ body >
</ html >
我将其放入.html
文件并将其保存在硬盘中。 一切都将在我的浏览器中的html文件中运行。
我希望页面是黑色的,没有页边空白,并且游戏应该位于屏幕中间。 因此,我进入<style>...</style>
并添加以下内容:
html , body {
height : 100% ;
margin : 0 ;
}
body {
background : black;
display : flex;
align-items : center;
justify-content : center;
}
/*Canvas is explained below*/
canvas {
border : 1px solid white;
}
2.游戏画布
普通的普通HTML存在一个问题:它是文本而非图形的标记语言。 非常适合按块,标题和表格逐字排列文本。 您可以使用HTML来告诉浏览器页面上需要哪些文本,浏览器将负责定位该文本,呈现该文本等。
(我们理所当然地认为,当我们浏览网络时,我们可以调整文本的大小,放大和缩小页面,使文本从一行到另一行等等。但是,实际上,它需要很多聪明的编程才能工作;浏览器非常出色。)
一旦我们尝试使用外部文本,那便是HTML出现问题的地方:
- 在页面上的特定位置放置对象并不是那么容易
- 在页面上移动对象并非易事
- 页面开始调整大小,移至移动设备或更改分辨率后,一切都会陷入困境。
简而言之,当您使用HTML时,您不能只是说:“请在坐标(X,Y)处绘制一个黑色正方形。” 您必须说些类似的话,“好吧,我们假设这个虚拟块是一个正方形,为其提供黑色背景,将其绝对定位在高度和宽度分别为100%的相对位置区域,并相对于顶部和顶部添加定位属性。让这个街区坐在某个地方。” 哦,无论如何它在一半的浏览器中都无法工作。
HTML是HTML的最新功能,画布是一种设计用于使用JavaScript绘制的元素。 您可以直接使用JavaScript来处理像素的空白。 使用JavaScript和canvas,您实际上可以说:“在坐标(X,Y)上给我画一个正方形”,您将获得一个简单,简单的正方形。
因此,让我们初始化一个空白画布:
<canvas id= "game" width= "400" height= "400" > </ canvas >
要控制画布,我们需要在下面的某个地方添加我们的JavaScript代码。 我们将该脚本放在<script>...</script>
标记之间。
我们需要做的第一件事是将画布包含在脚本中,以便我们可以使用画布。 我们用两行代码完成此操作:
// We basically link the canvas in our document to a variable in JavaScript…sort of. We’ll call that variable in order to work with canvas.
var canvas = document .getElementById( 'game' );
// Next, within the canvas, we want to create a two-dimensional space. So, we call the canvas and tell it to be 2D:
var context = canvas.getContext( '2d' );
从现在开始,为了在画布中绘制内容,我们将调用context并使用context可以执行的2D绘制方法。
此时,我们还需要做一些内务处理; 我们需要为我们的游戏设置变量:
// the size of a single unit in our grid. For now, the snake will run in blocks of 16 pixels. If we want a slimmer snake, we can set this number lower.
var grid = 16 ;
// The variable ‘count’ will contain a number that controls how fast the snake runs.
var count = 0 ;
// Our snake will be an object. This object will contain the snake’s speed, the coordinates of the head, and the array that corresponds to the snake’s body.
var snake = {
// Initial coordinates
x: 160 ,
y : 160 ,
// Initial speed. When the game starts, the snake will move horizontally, so its speed on the X axis will be equal to grid (which is 16). The snake’s Y speed will be zero because our snake won’t be moving up and down.
dx: grid,
dy : 0 ,
// ‘Cells’ will be an array containing all parts of the snake’s body (its tail?). At this point, the array is going to be empty.
cells: [],
// Now, let’s make the snake’s body start off with 4 cells.
maxCells: 4
};
// Here be food:
var apple = {
// The first food starts off at these coordinates:
x: 320 ,
y : 320
};
最后,让我们创建一个可以生成随机坐标的函数。 此功能将帮助我们的游戏定位下一个食物:
function getRandomInt ( min, max ) {
return Math .floor( Math .random() * (max - min)) + min;
}
3.主循环
主要循环是在游戏的每个帧处发生的事情。 该循环需要:
- 清除游戏画面
- 计算蛇的位置
- 如果蛇碰到了画布的边缘,请将蛇扭曲到画布的另一侧
- 推进蛇(虚拟和记忆)
- 放食物
- 画蛇的头和身体
- 检查蛇是否击中了自己
现在,让我们这样做:
function loop ( ) {
// The next function makes the game skip every 3 frames out of 4, effectively lowering the game speed to 15 frames per second. We need to do this to make the game playable.
requestAnimationFrame(loop);
if (++count < 4 ) {
return ;
}
count = 0 ;
// Next up: clearing the canvas.
context.clearRect( 0 , 0 ,canvas.width,canvas.height);
// Now, let’s move the snake.
snake.x += snake.dx;
snake.y += snake.dy;
// Warping snake if it hits edge of canvas on the X axis
if (snake.x < 0 ) {
snake.x = canvas.width - grid;
}
else if (snake.x >= canvas.width) {
snake.x = 0 ;
}
// Warping snake if it hits the edge on the Y axis
if (snake.y < 0 ) {
snake.y = canvas.height - grid;
}
else if (snake.y >= canvas.height) {
snake.y = 0 ;
}
// Advancing the snake’s head
snake.cells.unshift({ x : snake.x, y : snake.y});
// And removing the tail end of the snake’s body
if (snake.cells.length > snake.maxCells) {
snake.cells.pop();
}
// Planting the food
context.fillStyle = 'red' ;
context.fillRect(apple.x, apple.y, grid -1 , grid -1 );
// Setting the style of fills for the snake’s body
context.fillStyle = 'green' ;
// Filling each unit of the snake’s body
snake.cells.forEach( function ( cell, index ) {
// To make the game look retro, adding some black borders to the body cells
context.fillRect(cell.x, cell.y, grid -1 , grid -1 );
// If the snake reaches the apple…
if (cell.x === apple.x && cell.y === apple.y) {
// then increase the snake’s length,
snake.maxCells++;
// and plant new food.
apple.x = getRandomInt( 0 , 25 ) * grid;
apple.y = getRandomInt( 0 , 25 ) * grid;
}
// Checking to see if the snake hit itself
for ( var i = index + 1 ; i < snake.cells.length; i++) {
// If it did, then start over
if (cell.x === snake.cells[i].x && cell.y === snake.cells[i].y) {
// Resetting the game
snake.x = 160 ;
snake.y = 160 ;
snake.cells = [];
snake.maxCells = 4 ;
snake.dx = grid;
snake.dy = 0 ;
// Planting food at random
apple.x = getRandomInt( 0 , 25 ) * grid;
apple.y = getRandomInt( 0 , 25 ) * grid;
}
}
});
}
4.键盘控制
由于我们要使用键盘来控制蛇,因此我们需要游戏来聆听按键。 按下某个键后,游戏需要查看按下了哪个键,并做出相应的反应。
// Listen to the keys
document .addEventListener( 'keydown' , function ( e ) {
// First, we need to check if the key pressed is in the direction that the snake was already going. In that case, we can ignore the key.
// Left key
if (e.which === 37 && snake.dx === 0 ) {
snake.dx = -grid;
snake.dy = 0 ;
}
// Up key
else if (e.which === 38 && snake.dy === 0 ) {
snake.dy = -grid;
snake.dx = 0 ;
}
// Right key
else if (e.which === 39 && snake.dx === 0 ) {
snake.dx = grid;
snake.dy = 0 ;
}
// Down key
else if (e.which === 40 && snake.dy === 0 ) {
snake.dy = grid;
snake.dx = 0 ;
}
});
5.启动
我们需要做的就是开始我们先前编写的循环:
requestAnimationFrame(loop);
花一些时间来享受我们在这里创建的内容:
多么漂亮的复古街机游戏。
这是最终代码:
<!DOCTYPE html>
< html >
< head >
< title > The Snake </ title >
< style >
html , body {
height : 100% ;
margin : 0 ;
}
body {
background : black;
display : flex;
align-items : center;
justify-content : center;
}
canvas {
border : 1px solid white;
}
</ style >
</ head >
< body >
< canvas width = "400" height = "400" id = "game" > </ canvas >
< script >
var canvas = document .getElementById( 'game' );
var context = canvas.getContext( '2d' );
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
};
function getRandomInt ( min, max ) {
return Math .floor( Math .random() * (max - min)) + min;
}
function loop ( ) {
requestAnimationFrame(loop);
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 - 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 ;
}
snake.cells.unshift({ x : snake.x, y : snake.y});
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 ) {
context.fillRect(cell.x, cell.y, grid -1 , grid -1 );
if (cell.x === apple.x && cell.y === apple.y) {
snake.maxCells++;
apple.x = getRandomInt( 0 , 25 ) * grid;
apple.y = getRandomInt( 0 , 25 ) * grid;
}
for ( var i = index + 1 ; i < snake.cells.length; i++) {
if (cell.x === snake.cells[i].x && cell.y === snake.cells[i].y) {
snake.x = 160 ;
snake.y = 160 ;
snake.cells = [];
snake.maxCells = 4 ;
snake.dx = grid;
snake.dy = 0 ;
apple.x = getRandomInt( 0 , 25 ) * grid;
apple.y = getRandomInt( 0 , 25 ) * grid;
}
}
});
}
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 ;
}
});
requestAnimationFrame(loop);
</ script >
</ body >
</ html >
喜欢本教程吗? 随时浏览Practicum上的资源。 我们提供教育和指导,以帮助您提高技术技能并改善您的职业生涯。
From: https://hackernoon.com/making-the-snake-game-in-your-browser-practicum-by-yandex-5uw73ymc