在浏览器中制作蛇游戏:Yandex的实践

想制作自己的经典街机游戏吗?

我一直在观看有关教AI玩蛇的YouTube视频。 不用说,这很难。 同时, 自1976年以来 , Snake游戏就一直是对人类的正确挑战! 从哲学上讲,当我们自己玩游戏时,为什么还要教机器玩游戏呢?

因此,我决定制作Snake并玩它。

“但话又说回来,”我意识到,“当我可以将游戏基于别人的代码时,为什么要从头开始制作Snake?”

我在网上浏览了一个好的模板,然后找到了一个。 现在,我将引导您完成制作Snake的代码,并尽我所能向其解释。

该项目包含以下部分:

  1. HTML页面
  2. 游戏画布
  3. 主循环
  4. 键盘控制
  5. 发射

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值