【原创】《矩阵的史诗级玩法》连载八:利用矩阵变形创建斜45度地图

“拖延症患者”这一称号真的不是浪得虚名,不信你们看下连载七的发表时间,都是7个月前的了,真心感谢这几个月来一直支持我的那几个粉丝,让你们久等了。

上次(也就是7个月前,哈哈)我们把几个基础的变换矩阵给封装到了js里面,现在我们进入案例阶段,先把正常铺贴的地图绘制出来。我用的是Canvas,这样所有位置捕获都不基于dom对象了,而都是纯数学的东西。

<!DOCTYPE html>
<html>
<head>
<title>45度地图与矩阵</title>
</head>
<body>
<canvas width="800" height="800" id="canvas"></canvas>
</body>
<script>
	var canvas = document.getElementById("canvas");
	var context = canvas.getContext("2d");
	context.strokeStyle = "#0000cc";
	context.fillStyle = "#ccccff";
	context.lineWidth = 0.5;
	var gridNumX = 10;
	var gridNumY = 10;
	var unitSize = 40;
	for(var j = 0; j < gridNumY; j ++)
	{
		for(var i = 0; i < gridNumX; i ++)
		{
			var x = i * unitSize;
			var y = j * unitSize;
			//左上
			var leftTop = new Point(x, y);
			//右上
			var rightTop = new Point(x + unitSize, y);
			//右下
			var rightBottom = new Point(x + unitSize, y + unitSize);
			//左下
			var leftBottom = new Point(x, y + unitSize);
			//中间
			var center = new Point(x + unitSize * 0.5, y + unitSize * 0.5);
			context.fillStyle = "#ccccff";
			context.beginPath();
			context.moveTo(leftTop.x, leftTop.y);
			context.lineTo(rightTop.x + rightTop.y);
			context.lineTo(rightBottom.x, rightBottom.y);
			context.lineTo(leftBottom.x, leftBottom.y);
			context.closePath();
			context.stroke();
			context.fill();
			context.fillStyle = "#000";
			context.fillText(i + "," + j, center.x - 5, center.y + 5);//绘制编号,并根据字号做了个粗糙的修正
		}
	}	
</script>
</html>

新建一个文本文档,把以上代码保存为index.html,运行即可得到如下结果。

这是一个非常简单的铺贴,可能有人要问,为什么不直接用fillRect,这每根线都画,多麻烦啊。

原因是我等下就会改成斜铺了,我会用矩阵把这些正方形变成倾斜的菱形。而变换操作的对象并非一个完整的矩形,而是单个顶点。

现在我们引入上一次(7个月之前的,我现在说这句话都觉得别扭)编写好的js文件。

<script src="Matrix.js"></script>
<script src="MatrixUtil.js"></script>
<script src="Point.js"></script>

以上代码加入到<title>标签的下面即可。

然后,我们创建变换矩阵。现在,我们希望代码中的坐标是斜坐标系下的,也就是说,我们要根据这些斜坐标算出它们在直角坐标系中的位置,在连载4中,我们给出了变换过程:

等比缩放根号2/2倍->正旋转45度->横向拉伸2倍

有了矩阵,我们会发现这个变换的实现灰常简单,代码如下。

var matrix = new Matrix();
MatrixUtil.scale(matrix, Math.sqrt(2) / 2, Math.sqrt(2) / 2);
MatrixUtil.rotate(matrix, Math.PI / 4);
MatrixUtil.scale(matrix, 2, 1);

然后for循环部分对点进行转换:

var x = i * unitSize;
var y = j * unitSize;
//左上
var leftTop = new Point(x, y);
//右上
var rightTop = new Point(x + unitSize, y);
//右下
var rightBottom = new Point(x + unitSize, y + unitSize);
//左下
var leftBottom = new Point(x, y + unitSize);
//中间
var center = new Point(x + unitSize * 0.5, y + unitSize * 0.5);
context.fillStyle = "#ccccff";
context.beginPath();
var transformedLeftTop = matrix.transformPoint(leftTop);
context.moveTo(transformedLeftTop.x, transformedLeftTop.y);
var transformedRightTop = matrix.transformPoint(rightTop);
context.lineTo(transformedRightTop.x, transformedRightTop.y);
var transformedRightBottom = matrix.transformPoint(rightBottom);
context.lineTo(transformedRightBottom.x, transformedRightBottom.y);
var transformedLeftBottom = matrix.transformPoint(leftBottom);
context.lineTo(transformedLeftBottom.x, transformedLeftBottom.y);
context.closePath();
context.stroke();
context.fill();
context.fillStyle = "#000";
var transformedCenter = matrix.transformPoint(center);
context.fillText(i + "," + j, transformedCenter.x - 5, transformedCenter.y + 5);

调整后的全部js代码如下(用注释表示改动部分)

var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
context.strokeStyle = "#0000cc";
context.fillStyle = "#ccccff";
context.lineWidth = 0.5;
var gridNumX = 10;
var gridNumY = 10;
var unitSize = 40;
//以下4行为新增代码,创建Matrix并加入坐标转换
var matrix = new Matrix(); 
MatrixUtil.scale(matrix, Math.sqrt(2) / 2, Math.sqrt(2) / 2);	
MatrixUtil.rotate(matrix, Math.PI / 4);
MatrixUtil.scale(matrix, 2, 1);
for(var j = 0; j < gridNumY; j ++)
{
	for(var i = 0; i < gridNumX; i ++)
	{
		
			var x = i * unitSize;
			var y = j * unitSize;
			//左上
			var leftTop = new Point(x, y);
			//右上
			var rightTop = new Point(x + unitSize, y);
			//右下
			var rightBottom = new Point(x + unitSize, y + unitSize);
			//左下
			var leftBottom = new Point(x, y + unitSize);
			//中间
			var center = new Point(x + unitSize * 0.5, y + unitSize * 0.5);
			context.fillStyle = "#ccccff";
			context.beginPath();
			//以下8行为改动的代码,对点进行转换后再绘制
			var transformedLeftTop = matrix.transformPoint(leftTop);
			context.moveTo(transformedLeftTop.x, transformedLeftTop.y);
			var transformedRightTop = matrix.transformPoint(rightTop);
			context.lineTo(transformedRightTop.x, transformedRightTop.y);
			var transformedRightBottom = matrix.transformPoint(rightBottom);
			context.lineTo(transformedRightBottom.x, transformedRightBottom.y);
			var transformedLeftBottom = matrix.transformPoint(leftBottom);
			context.lineTo(transformedLeftBottom.x, transformedLeftBottom.y);
			context.closePath();
			context.stroke();
			context.fill();
			context.fillStyle = "#000";
			//以下两行为改动的代码,编号也要跟着转换
			var transformedCenter = matrix.transformPoint(center);
			context.fillText(i + "," + j, transformedCenter.x - 5, transformedCenter.y + 5);//根据字号做了个粗糙的修正
	}
}	

再次运行,效果如下图所示。

已经改成斜铺了,但是有一半被切走了,这是因为旋转始终基于0,0点,即左上角。为此,我们不妨在MatrixUtil.scale(matrix, 2, 1);一行后追加一个平移变换:

MatrixUtil.translate(matrix, 400, 0);

再次运行,完整的斜铺地图就出来了!

好了,45度斜铺完成,本来想继续说明如何根据鼠标位置判断选中哪块砖的,但看到篇幅开始有点长了,在这个浮躁的社会,大家容易没耐性看下去,那么我这篇就写到这里吧。

拖延了7个月,只怪上班太忙了,而且我从家里到公司有1.5小时的车程,还是地铁,哎,每天浪费3小时在车上,好蛋疼的说,幸好三维家可以让我做上自己喜欢的事情,累一点也值得。

在三维家上班一年了,我学习了更多跟图形方面相关的知识,比如布尔运算,图形三角化,面积计算,等等等等,有机会逐一分享给大家。

下篇会继续讲解如何根据位置判断对应的砖块,敬请期待!

猜你喜欢

转载自blog.csdn.net/iloveas2014/article/details/82927602