【问题标题】:HTML5 Canvas: Mouse and polygon collision detectionHTML5 Canvas:鼠标和多边形碰撞检测
【发布时间】:2011-04-06 12:34:43
【问题描述】:
所以我正在使用 HTML5 和 Javascript 制作塔防游戏。我唯一的问题是检测鼠标何时接触到攻击者的路径,这对于阻止玩家在路径上建造塔是必要的。攻击者的路径在 MAP.js 文件(见底部的链接)中由一个二维数组(一个包含 x 和 y 对的数组)确定,所以我必须处理的是一系列点连接时形成路径。我只是想禁止玩家在路径的 50 像素范围内放置塔。老实说,我在碰撞检测方面很糟糕,所以我们将不胜感激。
这里是所有代码的链接:
http://shapeshifting.comuv.com/Tower_Defense/td/
如您所想,只有 .js 文件适用,但大部分相关代码都在 objects.js 文件中。 (请见谅)
【问题讨论】:
标签:
javascript
html
canvas
collision-detection
【解决方案1】:
碰撞检测是游戏编码中那些古老的、隐藏的问题之一。通常人们会采用darkpenguin 的方法以某种方式预先计算静态地图上可以放置和不可放置的位置。下一步是想出一种方法来指定最有效的碰撞贴图。
您不希望您的游戏在响应用户移动鼠标时进行大量数学运算 - 它需要简短而快速 - 所以预先计算到快速的东西是至关重要的。
如果您的地图是网格,那么您的答案就在那里——碰撞地图是一个预先计算好的二维数组——基本上是一个非常小的黑白图像,网格上的每个位置都有一个像素。白色像素 (1) 可放置,黑色像素 (0) 不可放置。您只需使用这个真/假的二维数组作为查找。如果你想节省内存,你可以将网格上的每条 32 个空格捆绑成一个位标志。
如果您的地图不是网格,那么您仍然需要预先计算,但策略要复杂一些。第一种可能性是像 Hitesh 一样执行数学以生成分辨率稍高的碰撞图,然后其余的完全按照网格策略 - 例如,如果每个 4x4 像素块是一个碰撞条目,那么是否可以放置的是关于其坐标测试是否在足够多的 1 之上的测试 - 您可能需要 100% 的测试是 1,或者您可以让它们达到一点,假设 75% 的测试是 1。
如果这还不够详细,您可以进行这些更复杂的多边形测试,但您希望它们尽可能简单。当不使用预先计算的网格时,最简单的 2D 碰撞测试是 2 个圆 - 您只需计算它们中心之间的距离并检查它是否大于或小于它们的半径之和。如果您将怪物路径预先计算为一圈圈,下一步是将这些圈划分为……猜猜看……一个网格。这样可以避免每次检查都必须测试地图上的每个圆圈。这允许您在碰撞地图中拥有大量这些圆圈,因为碰撞测试首先是查找塔当前结束的网格条目,然后检查它是否仅与它最接近的圆圈发生碰撞,而不是整个地图。需要注意的是,这个预先计算的圆列表网格通常在多个相邻网格条目中具有相同的圆,因为包含给定圆的任何部分的每个网格条目都必须在其碰撞检查列表中包含该圆。
前 2 种网格方法的优点之一是,您可以非常轻松地自行进行 QA - 将碰撞贴图存储为图像并进行目视检查,以确保它看起来适合它所基于的贴图。不想写代码生成也可以手动画出来。
圆形方法为您提供了合法的曲线,并且可以产生更精细的碰撞边缘细节,但显然更难测试并确保没有地图有不良的碰撞贴图。写地图生成工具也比较费劲。
祝你好运!
【解决方案2】:
我会逐步解决这个问题。让我们看看你从什么开始。您有一条由点定义的路径——点对定义了一条线段。所以你真正拥有的是由线段组成的路径。当用户移动鼠标时,您将获得当前位置的 x,y 坐标。您要做的是找到鼠标点到所有线段的距离。如果它与任何线段的距离小于 50 像素,那么您不想让它们在那里构建。
要查找点和线段之间的距离,伪代码如下所示。假设A点和B点代表一条线段的两端,C点是鼠标点。
float distancePoint2LineSegment(Point a, Point b, Point c) {
Vector ab = b - a
Vector ac = c - a
Vector bc = c - b
float e = dotProduct(ac, ab)
if (e <= 0.0)
return sqrt(dotProduct(ac, ac))
float f = dotProduct(ab, ab)
if (e >= f)
return sqrt(dotProduct(bc, bc))
return sqrt(dotProduct(ac, ac) - e * e / f)
}
这将回答您的碰撞检测问题,但我认为您会想要查看性能。您的路径中有多少条线段,您是否想在每次用户移动鼠标时计算到每个线段的距离?您可以将线段放入四叉树中,这样您只需针对较少数量的线段测试鼠标点碰撞。