【原创】《矩阵的史诗级玩法》连载九:用矩阵反推45度地图直角坐标在斜坐标下的位置

我是个比较啰嗦的人,每次打算写一篇文章的时候都会发现太长了,从而被迫拆成两篇甚至更多。也好吧,至少让大家消化的容易一些。

上篇我们在给定斜坐标的情况下算出了直角坐标下的位置,从而实现了正方形砖块斜铺的效果。这篇我们要根据给定的直角坐标(如鼠标位置)算出它在斜坐标的哪个位置,从而确定鼠标位于哪个砖块上。

可以说这是一个逆向的操作,其做法已在连载四中提及,我们一起来回忆下:

横向缩小50%->负旋转45度->等比缩放根号2倍

也就是如下代码:

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

忽略平移的部分,我们来看看上篇的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);

你会发现,第一个矩阵的第n条变换都跟第二个矩阵的倒数第n条相对应,并且变换刚好互逆,也就是说,两个变换走的是同一条路,只是顺着走和反着走的区别。

自然而然的,为了对应x方向400的偏移,我们应该给matrixInvert在最开始加上如下的平移变换:

MatrixUtil.translate(matrixInvert, -400, 0);

接下来,我们监听一下canvas的鼠标移动事件mousemove,并获取它的直角坐标位置。

在js代码块追加如下代码:

canvas.addEventListener("mousemove", function(event)
{
	//鼠标位置
	var mouseX = event.clientX-canvas.getBoundingClientRect().left;
	var mouseY = event.clientY-canvas.getBoundingClientRect().top;
});

大家可以先console.log一下这个值是否正确再继续往下阅读。

然后我们用matrixInvert变换一下吧!

var transformedMouse = matrixInvert.transformPoint(new Point(mouseX, mouseY));

我们前面给砖块设置了40像素的宽高,所以要获取到它是第几块砖,就可以对算出来的xy进行%unitSize的操作得到。

var transformedMouse = matrixInvert.transformPoint(new Point(mouseX, mouseY));
var xIndex = Math.floor(transformedMouse.x / unitSize);
var yIndex = Math.floor(transformedMouse.y / unitSize);

最后用console.log输出一下看是否跟砖块的坐标对应上了。

console.log(xIndex, yIndex);

测试发现,能对上号了,不过这样看未免有点单调,我们不妨为它加上变色效果。当然了,这些已经不是dom对象,无法通过css神马的操作单个对象进行变色,必须重新绘制。为了不把话题扯远,我就不做类似于脏矩形的优化了,直接清空全部重画,大家不要在这事情上吐槽我哦。

我们先把for循环部分封装成一个方法。

function draw(xIndex, yIndex)
{
	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 = (i == xIndex && j == yIndex) ? "#ffeecc" : "#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);//根据字号做了个粗糙的修正
		}
	}	
}

//一开始要先绘制一次,并且不让任何编号的砖块都高亮
draw(-1, -1);

然后,mousemove事件函数追加draw的调用。

draw(xIndex, yIndex);

再次运行,移动鼠标,效果如下图所示。

至此,45度地图砖块的铺贴和选择功能就完成了。

45度地图由于广泛应用于游戏等热门领域,所以相信大家能百度到很多比本文简单很多的方法。没错,对于这个案例来说,用矩阵求解确实是大材小用。大家如果还记得连载四给出的最终结果,就会发现转换公式很简单,矩阵法似乎绕了很大一个圈子,就好比非要用puremvc做一个很简单的小游戏一样。

然而真的有不少人就是用puremvc来做小游戏,还理直气壮地说我这是练手,在这过程学习puremvc的原理和应用。本文自然也不例外,45度地图是矩阵的一个练手之作,抱着这样的心态,大家应该就能理解我的用意了吧。

另一方面,细看那些45度地图的文章,我们会发现,由于变换公式简单,所以他们往往都想当然地一笔带过。然后上层开发的朋友把公式拷过来,放到项目上发现能用就不求甚解,继续开发新的功能去。下次再用到,要么重新百度,要么从旧项目里拷过来,好点的封装成类库或者模板来重用,而在此过程中,可能大家都未必搞得懂他们的变换公式到底是怎么来的。

也有的文章使用向量的方法进行推导,在后续的教程中我也会给出向量的版本,不过最后我还是会转换到矩阵中进行变换。原因在于,后续的一些史诗级应用,矩阵的威力要比向量强大很多。至于是什么样的史诗级应用,我先卖个关子。饭要一口一口地吃,路要一步一步地走,步子大了容易扯着蛋,对吧。

本文提到了互逆地变换,在知道具体变换步骤地情况下,我们可以很轻松地找到逆变换的方法,但是在实际应用中,这些步骤并不会像这个demo一样清晰,已知的只有一个最终的变换矩阵,这时候想进行逆变换,要么想办法分解步骤,要么尝试找到可以直接进行逆变换的方法。显然后者要科学得多,因为前者不但计算繁琐,难以理解,而且容易造成精度损失,性能也不好,那我们何苦不选择后者呢?

而后者用到的,是一个可能大家都听过的玩意儿——逆矩阵,我会在下一篇文章中给出逆矩阵的推导方法并封装到我们的类里面,敬请期待!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值