使用数学(可能是线性代数)的JavaScript“像素” - 完美的碰撞检测旋转精灵

时间:2014-01-29 03:10:53

标签: javascript

我正在使用JavaScript制作2D游戏。为此,我需要能够“完美地”检查具有x / y位置(对应于它们的中心)的两个精灵之间的碰撞,以弧度表示的旋转,当然还有已知的宽度/高度。

经过数周的工作(是的,我甚至没有夸大其词),我终于提出了一个有效的解决方案,不幸的是,这个解决方案大约10,000x太慢,无法以任何有意义的方式进行优化。我完全放弃了从画布中实际绘制和读取像素的想法。这不是要削减它,但请不要让我详细解释原因。这需要通过数学和“想象的”2D世界/网格来完成,并且通过与众多人交谈,基本思想变得显而易见。但是,实际的实施并非如此。这就是我做和想做的事情:

我已经做了什么

在程序开始时,每个精灵都在其默认的直立位置进行像素查看,并且使用与图像的alpha通道对应的数据填充1维数组:实心像素由1表示,透明度为0.见图3.

背后的想法是那些1和0不再代表“像素”,而是“小数学球体彼此保持完美距离”,可以旋转而不会“丢失”或“添加”数据,就像发生的那样如果您以90度一次旋转图像,则使用像素。

我自然会先做快速的“边界框”检查,看看我是否应该打算精确计算。这个完成了。问题是罚款/“肯定”检查...

我无法弄明白

现在我需要确定精灵是否确实发生碰撞,我需要使用“线性代数”(我不知道)构建某种数学表达式,以确定这些“数据点的矩形”是否存在,正确定位和旋转,两者在重叠位置都有一个“1”。

虽然理论很简单,但实现这一目标所需的实用代码却超出了我的能力范围。我已经盯着代码看了好几个小时,问了很多人(并且有大量的问题清楚地解释了我的问题)并且真的付出了努力。现在我终于想放弃了。我非常非常感谢完成这项工作。我甚至不能通过使用图书馆来放弃和“欺骗”,因为我所发现的任何东西都无法从我能说出来解决这个问题。它们对我来说都是不可能理解的,并且似乎有完全不同的假设/要求。无论我做什么,似乎总是有些特殊情况。这很烦人。

这是该计划相关部分的伪代码:

function doThisAtTheStartOfTheProgram()
{
    makeQuickVectorFromImageAlpha(sprite1);
    makeQuickVectorFromImageAlpha(sprite2);
}

function detectCollision(sprite1, sprite2)
{
    // This easy, outer check works. Please ignore it as it is unrelated to the problem.
    if (bounding_box_match)
    {
        /*

            This part is the entire problem.
            I must do a math-based check to see if they really collide.

            These are the relevant variables as I have named them:

                sprite1.x
                sprite1.y
                sprite1.rotation // in radians
                sprite1.width
                sprite1.height
                sprite1.diagonal // might not be needed, but is provided

                sprite2.x
                sprite2.y
                sprite2.rotation // in radians
                sprite2.width
                sprite2.height
                sprite2.diagonal // might not be needed, but is provided

                sprite1.vectorForCollisionDetection
                sprite2.vectorForCollisionDetection

            Can you please help me construct the math expression, or the series of math expressions, needed to do this check?
            To clarify, using the variables above, I need to check if the two sprites (which can rotate around their centre, have any position and any dimensions) are colliding. A collision happens when at least one "unit" (an imagined sphere) of BOTH sprites are on the same unit in our imaginated 2D world (starting from 0,0 in the top-left).

        */

        if (accurate_check_goes_here)
            return true;
    }

    return false;
}

换句话说,“exact_check_goes_here”是我想知道应该是什么。当然,它不需要是单个表达式,我非常希望看到它在“步骤”(带注释!)中完成,以便我有机会理解它,但请不要将其视为“勺子喂”。我完全承认我很擅长数学,这超出了我的能力范围。这只是一个事实。我想继续前进并研究我自己可以实际解决的问题。

澄清:由于性能,1D阵列是1D而不是2D。事实证明,速度在JS World中非常重要。 虽然这是一个非盈利的项目,完全是为了私人满意,但我没有时间和精力去订购,坐下来阅读一些数学书籍并从头开始学习。我不骄傲缺乏能帮助我很多的数学技能,但在这一点上,我需要完成这个游戏,否则我会发疯。这个特殊的问题使我无法长时间地完成任何其他工作。

我希望我已经很好地解释了这个问题。然而,最令人沮丧的感受之一是,当人们发送善意的回复时,不幸的是,这些回复显示帮助的人没有阅读过这个问题。我不是在侮辱你们所有人 - 我希望这次不会发生这种情况!对不起,如果我的描述很差。我真的尽力做到非常清楚。

好的,所以我需要“声望”才能发布我花时间创建的插图来说明我的问题。所以我转而链接到它们:

插图

  1. (由Stackoverflow审查)
  2. (由Stackoverflow审查)
  3. http://i.imgur.com/0q1ucnn.png
  4. 行。这个网站不会让我甚至链接到图像。只有一个。然后我会挑选最重要的一个,但是如果我可以链接到其他人那将会有很大的帮助......

4 个答案:

答案 0 :(得分:4)

首先,您需要了解使用单个/简单方程无法检测此类碰撞。因为精灵的形状很重要,并且这些是由Width x Height = Area位数组描述的。因此,算法的最坏情况复杂度必须至少为O(区域)。

我将如何做到这一点:

以两种方式表示精灵:

1)位图,指示像素不透明的位置,

2)不透明像素的坐标列表。 [可选,对于加速,如果是空心精灵。]

选择具有最短像素列表的精灵。找到将此精灵的局部坐标转换为另一个精灵的局部坐标的刚性变换(平移+旋转)(这是线性代数发挥作用的地方 - 旋转是角度的差异,平移是两者之间的向量左上角 - 见http://planning.cs.uiuc.edu/node99.html)。

现在扫描不透明像素列表,将像素的局部坐标转换为另一个精灵的局部坐标。通过查找位图表示来检查是否落在不透明像素上。

最差O(不透明区域)坐标变换+像素测试,这是最佳的。

如果精灵被放大(大像素),作为第一个近似值,您可以忽略缩放。如果您需要更高的精度,可以考虑每像素采样几个点。精确计算将涉及方形/方形碰撞交叉算法(具有旋转),更复杂且成本更高。请参阅http://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm

答案 1 :(得分:3)

这是一个精确的解决方案,无论像素大小(缩放或不缩放)都可以使用。

使用位图表示(每个像素1个不透明度位)和分解为正方形或矩形(矩形是可选的,只是一个优化;单个像素就可以了)。

依次处理(源)精灵的所有矩形。通过旋转/平移,将矩形映射到另一个精灵(目标)的坐标空间。您将获得一个覆盖在像素网格上的旋转矩形。

现在,您将使用扫描线算法填充此矩形:首先使用水平线穿过矩形顶点将矩形分成三个(两个三角形和一个平行四边形)。对于三个独立的形状,找到跨越它们的所有水平像素线(这可以通过查看Y值的范围来完成)。对于每个这样的水平线,计算两个交叉点。然后找到落在两个交点之间的所有像素角(X值的范围)。对于在矩形内部有角的任何像素,查找(目标)精灵位图中的相应位。

没有太难编程,没有复杂的数据结构。计算工作量与每个源矩形所覆盖的目标像素数大致成比例。

答案 2 :(得分:1)

虽然你已经说过你不觉得渲染到画布并检查数据是否可行解决方案,但我想提出一个可能已经或可能没有发生过的想法,哪些应该是合理有效。

此解决方案依赖于将具有半不透明度的任何像素渲染到画布两次将导致完全不透明度的像素。步骤如下:

  • 调整测试画布的大小,使两个精灵都适合它(这也将清除画布,因此每次需要测试碰撞时都不必创建新元素。)
  • 转换精灵数据,使任何具有任何不透明度或颜色的像素在不透明度为50%时设置为黑色。
  • 将精灵渲染到适当的距离和相对位置。
  • 循环生成的画布数据。如果任何像素的不透明度为100%,则检测到碰撞。返回true。
  • 否则,返回false。
  • 洗涤,冲洗,重复。

此方法应该运行得相当快。现在,为了优化 - 这里的瓶颈可能是最终的不透明度检查(尽管将图像渲染到画布可能很慢,可能正在清除/调整它的大小):

  • 通过更改循环中通过最终数据像素的增量来降低最后一步中不透明度检测的分辨率。
  • 从中间向上和向下循环,而不是从上到下循环(并在发现任何单个碰撞时立即返回)。这样,您在循环中遇到任何碰撞的可能性就会更高,从而减少其长度。

我不知道你的限制是什么以及为什么你不能渲染到画布,因为你拒绝对此发表评论,但希望这种方法对你有用。如果不是,也许它可能会对未来的用户派上用场。

答案 3 :(得分:0)

请查看以下想法是否适合您。在这里,我创建了一个线性阵列的点,对应于两个精灵中每个精灵中设置的像素。然后我旋转/平移这些点,给我两组坐标,用于各个像素。最后,我检查彼此的像素,看看是否有任何一对在1的距离内 - 这是“碰撞”。

你可以显然添加你的精灵的一些分段(只测试“边界像素”),测试边界框,并做其他事情来加快速度 - 但它实际上非常快(一旦你拿走所有的{{1}那些声明只是确认事情的行为......)。请注意,我测试console.log() - 如果它太大,则无需计算整个距离。另外,我不需要平方根来知道距离是否小于1.

我不确定在dx函数中使用new array()是否会导致内存泄漏问题。如果你每秒运行30次这个功能,那就要看一下......

pixLocs