返回 导航

HTML5 / CSS3

hangge.com

HTML5 - 使用Canvas开发迷宫游戏(附在线demo)

作者:hangge | 2016-02-27 09:00
本文介绍如何使用Canvas画布制作一个简单的迷宫游戏:用户按下方向键时,笑脸图标会沿相应的方向移动(移动时有动画效果),遇到墙壁时(碰撞检测)就会停下来。

1,在线样例

2,实现原理
(1)迷宫的生成:我们这里是使用一幅迷宫图片,然后把整幅图绘制到画布上。
(2)通过设置 window.onkeydown 的响应函数 processKey(),当用户按下方向键的时候,根据键码调整笑脸各方向上的速度。
(3)drawFrame() 函数每10毫秒刷新画布,如果笑脸有速度则移动并绘制轨迹,同时作碰撞检测。最后还会检测笑脸是否到底部,到了话弹出成功消息框。

3,碰撞检测
这里使用的是基于像素颜色碰撞检测,我们取笑脸所在区域的像素块(在笑脸四周再稍微扩展一点),判断其中是否有黑色像素,有的话则说明撞墙了。

4,完整代码
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>hangge.com</title>
  <style>
	canvas {
	  border: 6px double black;
	  background: white;
	}

	img {
	  display: none;
	}

	button {
	  padding: 3px;
	}
  </style>
  <script>
	var canvas;
	var context;

	// 记录笑脸图标的当前位置
	var x = 0;
	var y = 0;

	// 记录笑脸在x轴和y轴方向上每一帧要移动多少像素
	var dx = 0;
	var dy = 0;

	window.onload = function() {
	  // 设置画布
	  canvas = document.getElementById("canvas");
	  context = canvas.getContext("2d");

	  // 绘制迷宫背景
	  drawMaze('http://www.hangge.com/blog_uploads/201602/2016022313460193716.png', 5, 5);

	  // 当用户按下键盘上的键时,运行processKey()函数
	  window.onkeydown = processKey;
	};


	// timer引用,这样重新加载迷宫的时候可以很方便地停止绘制
	var timer;

	function drawMaze(mazeFile, startingX, startingY) {
	  // 先停止绘制
	  clearTimeout(timer);

	  // 停止笑脸
	  dx = 0;
	  dy = 0;

	  // 加载迷宫图片
	  var imgMaze = new Image();
	  imgMaze.onload = function() {
		// 调整画布大小以适应迷宫图片
		canvas.width = imgMaze.width;
		canvas.height = imgMaze.height;

		// 绘制迷宫
		context.drawImage(imgMaze, 0,0);

		// 绘制笑脸
		x = startingX;
		y = startingY;

		var imgFace = document.getElementById("face");
		context.drawImage(imgFace, x, y);
		context.stroke();

		// 10毫秒后绘制下一帧
		timer = setTimeout(drawFrame, 10);
	  };
	  imgMaze.src = mazeFile;
	}


	function processKey(e) {
	  // 如果笑脸在移动,停止
	  dx = 0;
	  dy = 0;

	  // 按下了向上键,向上移动
	  if (e.keyCode == 38) {
		dy = -1;
	  }

	  // 按下了向下键,向下移动
	  if (e.keyCode == 40) {
		dy = 1;
	  }

	  // 按下了向左键,向左移动
	  if (e.keyCode == 37) {
		dx = -1;
	  }

	  // 按下了向右键,向右移动
	  if (e.keyCode == 39) {
		dx = 1;
	  }
	}

	function checkForCollision() {
	  // 取得笑脸所在的像素块,再稍微扩展一点
	  var imgData = context.getImageData(x-1, y-1, 15+2, 15+2);
	  var pixels = imgData.data;

	  // 检测其中的像素
	  for (var i = 0; n = pixels.length, i < n; i += 4) {
		var red = pixels[i];
		var green = pixels[i+1];
		var blue = pixels[i+2];
		var alpha = pixels[i+3];

		// 检测黑色的墙(如果检测到,说明撞墙了)
		if (red == 0 && green == 0 && blue == 0) {
		  return true;
		}
		// 检测灰色的边(如果检测到,说明撞墙了)
		if (red == 169 && green == 169 && blue == 169) {
		  return true;
		}
	  }
	  // 没有撞墙
	  return false;
	}


	function drawFrame() {
	  // 检测笑脸是否正在哪个方向上移动,如果不是,则什么也不做。
	  if (dx != 0 || dy != 0) {
		// 在笑脸当前位置绘制一块黄色背景(表示移动痕迹),然后把笑脸移到下一个位置
		context.beginPath();
		context.fillStyle = "rgb(254,244,207)";
		context.rect(x, y, 15, 15);
		context.fill()

		// 增大位置值
		x += dx;
		y += dy;

		// 碰撞检测(撞墙的话笑脸需要放回上一个位置并停止移动)
		if (checkForCollision()) {
		  x -= dx;
		  y -= dy;
		  dx = 0;
		  dy = 0;
		}

		// 这里可以绘制笑脸
		var imgFace = document.getElementById("face");
		context.drawImage(imgFace, x, y);

		// 检测笑脸是否已经到达迷宫底部,是的话提示成功
		if (y > (canvas.height - 17)) {
		  alert("You win!");
		  return;
		}
	  }

	  // 10毫秒后绘制下一正
	  timer = setTimeout(drawFrame, 10);
	}

	function loadEasy() {
	  drawMaze('http://www.hangge.com/blog_uploads/201602/2016022313460193716.png', 5, 5);
	}

	function loadHard() {
	  drawMaze('http://www.hangge.com/blog_uploads/201602/2016022313455480577.png', 268, 5);
	}
  
  </script>
</head>

<body>    
  <canvas id="canvas">
  </canvas>
  <div>
    <button onclick="loadHard()">加载困难迷宫</button>
    <button onclick="loadEasy()">加载简单迷宫</button>
  </div>

  <img id="face" src="http://www.hangge.com/blog_uploads/201602/2016022313454739977.png">
</body>
</html>
评论

全部评论(1)

回到顶部