使用画布的等距平铺地图(带有点击检测)

Posted

技术标签:

【中文标题】使用画布的等距平铺地图(带有点击检测)【英文标题】:Isometric tilemap using canvas (with click detection) 【发布时间】:2017-01-14 23:22:13 【问题描述】:

我目前正在开发一款游戏,它需要一张由各种平铺图像组成的地图。我设法让它们正确显示(见第二张图片),但我现在不确定如何从鼠标位置计算点击的图块。

是否有任何现有的库用于此目的?

另请注意,平铺图像并非完美地绘制为“面向角的相机”,它们是顺时针略微旋转的。

【问题讨论】:

【参考方案1】:

等距变换

定义投影

等轴测显示与标准显示相同,唯一改变的是 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 = 2y = 1(第一个顶部块是 x = 0y = 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;

所以传递一个点并返回一个转换点。结果参数允许您传递现有点,如果您经常这样做,则无需分配新点。

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 渲染都使用它们,并且深入了解它们很重要如果你想进入计算机可视化。

【讨论】:

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

以上是关于使用画布的等距平铺地图(带有点击检测)的主要内容,如果未能解决你的问题,请参考以下文章

OpenLayers 导出带有比例尺的 png 地图画布

PS中如何将各图层的内容按照画布大小等距离分布排列?谢谢

canvas画布上平铺图片&&绘制文本

canvas画布上平铺图片&&绘制文本

单击画布中的地图

小程序九:导航&地图&画布