1. 程式人生 > >《矩陣的史詩級玩法》連載八:利用矩陣變形建立斜45度地圖

《矩陣的史詩級玩法》連載八:利用矩陣變形建立斜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);
			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();
		}
	}	
</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);
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();

調整後的全部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);
		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();
	}
}	

再次執行,效果如下圖所示。

已經改成斜鋪了,但是有一半被切走了,這是因為旋轉始終基於0,0點,即左上角。為此,我們不妨在MatrixUtil.scale(matrix, 2, 1);一行後追加一個平移變換:

MatrixUtil.translate(matrix, 400, 0);

再次執行,完整的斜鋪地圖就出來了!

好了,45度斜鋪完成,本來想繼續說明如何根據滑鼠位置判斷選中哪塊磚的,但看到篇幅開始有點長了,在這個浮躁的社會,大家容易沒耐性看下去,那麼我這篇就寫到這裡吧。

拖延了7個月,只怪上班太忙了,而且我從家裡到公司有1.5小時的車程,還是地鐵,哎,每天浪費3小時在車上,好蛋疼的說,幸好三維家可以讓我做上自己喜歡的事情,累一點也值得。

在三維家上班一年了,我學習了更多跟圖形方面相關的知識,比如布林運算,圖形三角化,面積計算,等等等等,有機會逐一分享給大家。

下篇會繼續講解如何根據位置判斷對應的磚塊,敬請期待!