Javascript 使用画布的等轴测平铺贴图(带有点击检测)

Javascript 使用画布的等轴测平铺贴图(带有点击检测),javascript,html,math,canvas,Javascript,Html,Math,Canvas,我目前正在开发一个游戏,它需要一个由各种瓷砖图像组成的地图。我设法使它们正确显示(参见第二幅图),但我现在不确定如何从鼠标位置计算单击的平铺 是否有任何现有的图书馆用于此目的 还请注意,瓷砖图像并不是完美地“面向角落的相机”绘制的,它们是稍微顺时针旋转的 等距变换 定义投影 等轴测显示与标准显示相同,唯一改变的是x轴和y轴的方向。通常,x轴被定义为(1,0)跨一个单位并向下归零,y轴被定义为(0,1)跨一个单位并向下归零。对于等轴测(严格来说,您的图像是一个二次投影),您将有类似于x轴(0.5,

我目前正在开发一个游戏,它需要一个由各种瓷砖图像组成的地图。我设法使它们正确显示(参见第二幅图),但我现在不确定如何从鼠标位置计算单击的平铺

是否有任何现有的图书馆用于此目的

还请注意,瓷砖图像并不是完美地“面向角落的相机”绘制的,它们是稍微顺时针旋转的

等距变换 定义投影

等轴测显示与标准显示相同,唯一改变的是x轴和y轴的方向。通常,x轴被定义为(1,0)跨一个单位并向下归零,y轴被定义为(0,1)跨一个单位并向下归零。对于等轴测(严格来说,您的图像是一个二次投影),您将有类似于x轴(0.5,1)和y轴(-1,0.5)的东西

矩阵

由此,您可以创建一个渲染矩阵,其中6个值,两个轴各两个,原点两个,我将暂时忽略(原点),只使用4作为轴,并假设原点始终为0,0

var dimetricMatrix = [0.5,1.0,-1,0.5]; // x and y axis
矩阵变换

从中可以在显示器上获得与给定等轴测坐标匹配的点。假设块是200 x 200像素,并且您通过块x和y来寻址每个块。因此,图像底部的块位于
x=2
y=1
(第一个顶部块是
x=0
y=0

利用矩阵可以得到块的像素位置

var blockW = 200;
var blockH = 200;
var locX = 2;
var locY = 1;

function getLoc(x,y){
   var xx,yy; // intermediate results
   var m = dimetricMatrix; // short cut to make code readable
   x *= blockW; // scale up
   y *= blockH;
   // now move along the projection x axis 
   xx = x * m[0];
   yy = x * m[1];   
   // then add the distance along the y axis
   xx += y * m[2];
   yy += y * m[3];

   return {x : xx, y : yy};
}
在我继续之前,您可以看到我已按块大小缩放了x和y。我们可以简化上述代码,并在矩阵中包含刻度200200

var xAxis = [0.5, 1.0];
var yAxis = [-1, 0.5];
var blockW = 200;
var blockH = 200;
// now create the matrix and scale the x and y axis
var dimetricMatrix = [
    xAxis[0] * blockW,
    xAxis[1] * blockW,
    yAxis[0] * blockH,
    yAxis[1] * blockH,

]; // x and y axis   
矩阵保持x轴和y轴的比例,因此x轴的两个数字告诉我们变换单元的方向和长度

简化功能

并重新执行
getLoc
功能以提高速度和效率

function transformPoint(point,matrix,result){
   if(result === undefined){
       result = {};
   }
   // now move along the projection x axis 
   result.x = point.x * matrix[0] + point.y * matrix[2];
   result.y = point.x * matrix[1] + point.y * matrix[3];
   return result;
}
所以,传递一个点,然后得到一个变换后的点。result参数允许您传递一个现有点,如果您经常这样做,那么就不必分配新点

var point = {x : 2, y : 1};
var screen = transformPoint(point,dimetricMatrix);
// result is the screen location of the block

// next time
screen = transformPoint(point,dimetricMatrix,screen); // pass the screen obj
                                                      // to avoid those too
                                                      // GC hits that kill
                                                      // game frame rates
将矩阵倒置

所有这些都很方便,但你需要与我们刚才做的相反。幸运的是,矩阵的工作方式允许我们通过反转矩阵来反转过程

function invertMatrix(matrix){
    var m = matrix; // shortcut to make code readable
    var rm = [0,0,0,0]; // resulting matrix
    // get the cross product of the x and y axis. It is the area of the rectangle made by the
    // two axis 
    var cross =  m[0] * m[3] - m[1] * m[2]; // I call it the cross but most will call 
                                                    // it the determinate (I think that cross 
                                                    // product is more suited to geometry while
                                                    // determinate is for maths geeks)
    rm[0] = m[3] / cross;   // invert both axis and unscale (if cross is 1 then nothing)
    rm[1] = -m[1] / cross;
    rm[2] = -m[2] / cross;
    rm[3] = m[0] / cross;    
    return rm;         
}
现在我们可以反转我们的矩阵

var dimetricMatrixInv = invertMatrix(dimetricMatrix); // get the invers
现在我们有了逆矩阵,我们可以使用变换函数将屏幕位置转换为块位置

var screen = {x : 100, y : 200};
var blockLoc = transformPoint(screen, dimetricMatrixInv );
// result is the location of the block
用于渲染的矩阵

变换矩阵
dimetricMatrix
也可以被2D画布使用,但您需要添加原点

var m = dimetricMatrix;
ctx.setTransform(m[0], m[1], m[2], m[3], 0, 0); // assume origin at 0,0
现在,您可以在块周围使用

ctx.strokeRect(2,1,1,1); // 3rd by 2nd block 1 by 1 block wide.
来源


我在上面的所有内容中都省略了起源,我将把它留给你去查找,因为在线上有一万亿页关于矩阵的内容,因为所有2D和3D渲染都使用它们,如果你想进入计算机可视化领域,对它们有很好的深入了解是很重要的

旧帖子,但仍然非常有用。泰。