【问题标题】:How to calculate the index of the tile underneath the mouse in an isometric world taking into account tile elevation考虑到瓷砖高程的情况下,如何在等距世界中计算鼠标下方瓷砖的索引
【发布时间】:2016-06-28 22:19:21
【问题描述】:

我有一个基于图块的等距世界,我可以使用以下计算来计算哪个图块位于特定(鼠标)坐标下方:

function isoTo2D(pt:Point):Point{
  var tempPt:Point = new Point(0, 0);
  tempPt.x = (2 * pt.y + pt.x) / 2;
  tempPt.y = (2 * pt.y - pt.x) / 2;
  return(tempPt);
}

function getTileCoordinates(pt:Point, tileHeight:Number):Point{
  var tempPt:Point = new Point(0, 0);
  tempPt.x = Math.floor(pt.x / tileHeight);
  tempPt.y = Math.floor(pt.y / tileHeight);
  return(tempPt);
}

(取自http://gamedevelopment.tutsplus.com/tutorials/creating-isometric-worlds-a-primer-for-game-developers--gamedev-6511,这是一个flash实现但数学是一样的)

但是,当我的瓷砖具有不同的海拔高度时,我的问题就出现了:

在这些情况下,由于某些标高较高的图块的高度,后面的图块(或部分图块)被遮盖了,不应该被鼠标选中,而是选择了在它的前面。 考虑到瓷砖的高程,如何通过鼠标坐标计算瓷砖?

我正在使用 javascript 和画布实现。

【问题讨论】:

  • 好吧,您的瓷砖位于 3D 平面上,而您的鼠标位于 2D 平面上,因此您可能需要使用您正在查看瓷砖的角度进行一些线性代数投影。跨度>
  • 我没有使用 3d 平面 - 这是一个模仿 3d 平面的 2d 平面
  • 模仿部分很重要。假装你可以“旋转”你的世界。然后你就能看到不同的瓷砖,对吧?向上倾斜角度,以便您直视下方?然后您可以轻松找到鼠标所在的磁贴。因此,您要么考虑倾斜和旋转角度,要么考虑一些用于鼠标进入和离开事件的边界框侦听器,或者完全放弃鼠标,只使用键盘选择一个图块。

标签: javascript isometric


【解决方案1】:

奇怪的是,答案写在the Wikipedia page 的标题为“将屏幕映射到世界坐标”的部分中。与其尝试描述图形,不如将这一节读三遍。

您需要准确确定您使用的是哪种同构投影,通常是用尺子测量屏幕上的图块大小。

【讨论】:

  • 如果我正确理解了这篇文章,那正是我用来计算图块位置的方法。但是,我的问题是当某些瓷砖具有较高的高度时,瓷砖的侧面以及瓷砖本身必须可以通过鼠标选择,并且如果隐藏在后面的瓷砖后面应该无法选择另一个。
  • 为此没有捷径;您必须计算可能位于的整组图块,然后取最靠前的图块,通常是具有最低行 + col 的图块。最小化的确切函数将取决于您的投影和编号,但将是 (+/-) 行 (+/- col) 的(最小值或最大值)。 “便宜”的方法是使用屏幕上或调色板中的 z 值来编码图块编号。
【解决方案2】:

您的命中测试函数需要访问您的所有图块才能确定哪个图块被命中。然后它将从最高海拔开始执行测试命中。

假设您只有离散的(整数)图块高度,一般算法将是这样的(伪代码,假设 tiles 是具有高度属性的对象的二维数组):

function getTile(mousePt, tiles) {
    var maxElevation = getMaxElevation(tiles);
    var minElevation = getMinElevation(tiles);
    var elevation;
    for (elevation = maxElevation; elevation >= minElevation; elevation--) {
        var pt = getTileCoordinates(mousePt, elevation);
        if (tiles[pt.x][pt.y].elevation === elevation) {
            return pt;
        }
    }
    return null; // not tile hit
}

此代码需要针对任意高程进行调整,并且可以进行优化以跳过不包含任何图块的高程。

请注意,我的伪代码忽略了图块的垂直边,单击它们将选择被垂直边遮挡的(低海拔)图块。如果需要考虑垂直瓦片,则需要更通用的表面撞击检测方法。您可以访问每个图块(从最近到最远)并测试鼠标坐标是在“屋顶”中还是在面向“墙”多边形的查看器中。

【讨论】:

  • 这是一个有趣的方法。但是,高架瓷砖的侧面可以做什么?我的第一个想法是检查鼠标是否在高架瓷砖和地面上的同一瓷砖之间的矩形内,但是如果前面的高架瓷砖不那么高,它就会工作不正确,选择后面的瓷砖(它有更高高度)而不是正确选择前面的瓷砖(略低的高度)。
  • 你是对的,这种方法不解决点击垂直(“墙壁”)表面,而只处理点击水平(“屋顶”)表面。问题描述没有说明单击“墙”时的行为应该是什么。它应该是 noop 还是与单击该瓷砖的“屋顶”相同?我选择了“简单”的行为并将墙壁视为点击。 ;-)
  • 应该和点击瓷砖屋顶一样。
【解决方案3】:

有一种技术可以在canvas 上捕获鼠标下方的对象,而无需将鼠标坐标重新计算到您的“世界”坐标中。这并不完美,有一些缺点和限制,但在一些简单的情况下它可以发挥作用。

1) 将另一个 canvas 放置在主 canvas 的顶部,并将其 opacity 设置为 0。确保您的第二个 canvas 具有相同的大小并与您的主 canvas 重叠。

2) 每当您将交互式对象绘制到主 canvas 时,在第二个画布上绘制并填充相同的对象,但每个对象使用一种唯一颜色(从 #000000#ffffff

3) 将鼠标事件处理设置为第二个画布。

4) 在鼠标位置的第二个canvas 上使用getPixel 来获取点击/悬停的对象的“id”。

主要优点是WYSIWYG 原则,所以(如果一切都正确完成)您可以确定,主要canvas 上的对象与第二个canvas 上的对象在同一个位置,所以您不需要需要担心画布调整大小或对象depth(如您的情况)计算以获得正确的对象。

主要缺点是需要“双重渲染”整个场景,但可以通过在不需要时不在第二个画布上绘制来优化它,例如:

  • 处于“空闲”场景状态,当交互式对象停留在原位并等待用户操作时。

  • 处于“锁定”场景状态,当某些东西被动画或smth 时。并且不允许用户与对象交互。

主要限制是场景中交互对象的最大数量(最多 #ffffff 或 16777215 个对象)。

所以...不推荐:

  • 场景中有大量交互式对象的游戏。 (表现不佳)

  • 快节奏游戏,其中交互式对象不断移动/创建/销毁。(性能不佳,重复使用 id 的问题)

适合:

  • GUI 的处理

  • 回合制游戏/慢节奏益智游戏。

【讨论】:

【解决方案4】:

如果地图不可旋转且与您在此处发布的图片完全相同,

当您绘制多边形时,将每个图块的多边形保存在一个多边形数组中。然后使用它们(它们的瓦片)到您的距离(最接近,最后最远)对数组进行一次排序,同时保持它们按瓦片索引分组。

当点击事件发生时,获取鼠标的x,y坐标,并从第一个元素数组到最后一个元素进行point in polygon测试。当被击中时,停在那个元素上。

无论一个图块有多高,都不会隐藏任何离你更近(甚至与你相同距离)的图块。

多边形测试中的点已经解决了:

Point in Polygon Algorithm

How can I determine whether a 2D Point is within a Polygon?

Point in polygon

您甚至可以使用此函数检查画布的每个像素一次并将结果保存到二维点数组 vect2[x][y] 中,它给出了鼠标 x,y 坐标的 i,j 瓷砖索引,然后使用这是一个非常快速的索引查找器。

优点:

  • 使用 webworker 快速且可并行化(如果有数百万个图块)
  • 使用按与您的距离排序的多边形数组数组可扩展到多个等距地图。
  • 高度不会降低性能,因为每个图块最多只有 3 个。
  • 不需要将等轴测图转换为 2d。只是画布上多边形角的坐标和同一画布上鼠标的坐标。

缺点:

  • 如果您还没有,您需要每个角的坐标。
  • 点击一个角会选择离您最近的瓷砖,同时它在四个瓷砖上。

【讨论】:

    猜你喜欢
    • 2019-11-20
    • 2023-03-02
    • 1970-01-01
    • 2021-10-22
    • 2021-05-23
    • 2016-11-19
    • 1970-01-01
    • 1970-01-01
    • 2019-07-04
    相关资源
    最近更新 更多