使用画布的等距平铺地图(带有点击检测)
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 = 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;
所以传递一个点并返回一个转换点。结果参数允许您传递现有点,如果您经常这样做,则无需分配新点。
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 渲染都使用它们,并且深入了解它们很重要如果你想进入计算机可视化。
【讨论】:
旧帖子,但仍然非常有用。泰。 ??以上是关于使用画布的等距平铺地图(带有点击检测)的主要内容,如果未能解决你的问题,请参考以下文章