用 Javascript 做一个贪食蛇的游戏


贪食蛇游戏很有趣, 而且也不复杂, 是学习一门编程语言的最好的入门练手的项目. 我在加入GE后接触到GE开发的Magik语言(Wiki), 便很快的用它写了一个贪吃蛇的游戏.

snake-game-in-magik-programming 用 Javascript 做一个贪食蛇的游戏 教程 游戏 程序设计

snake-game-in-magik-programming

今天, 我们就来用当今最火的语言 Javascript 来写一下, 你就会发现, 其实这个游戏很简单就能实现了.

simple-snake-game-in-javascript 用 Javascript 做一个贪食蛇的游戏 教程 游戏 程序设计

simple-snake-game-in-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?

GD Star Rating
loading...
本文一共 631 个汉字, 你数一下对不对.
用 Javascript 做一个贪食蛇的游戏. (AMP 移动加速版本)
上一篇: 英国 stagecoach 表演课真是又贵又费时的课外兴趣班啊
下一篇: Microbit 游戏编程: 贪心算法也无法让贪吃蛇永生

扫描二维码,分享本文到微信朋友圈
4fe4cf424d91dfeb157773f2656bd457 用 Javascript 做一个贪食蛇的游戏 教程 游戏 程序设计

评论