《矩陣的史詩級玩法》連載八:利用矩陣變形建立斜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小時在車上,好蛋疼的說,幸好三維家可以讓我做上自己喜歡的事情,累一點也值得。
在三維家上班一年了,我學習了更多跟圖形方面相關的知識,比如布林運算,圖形三角化,面積計算,等等等等,有機會逐一分享給大家。
下篇會繼續講解如何根據位置判斷對應的磚塊,敬請期待!