如何用Rabbit进行像素完美碰撞检测?

时间:2012-05-13 14:31:34

标签: python collision-detection pyglet

我已经开始研究Rabbyt库了,到目前为止,我真的非常喜欢将它与pyglet结合使用。

在库中似乎没有实现的一件事是精灵之间的像素完美碰撞检测。我在实现它时遇到两个问题。

首先,我使用pyglet加载sprite的纹理,但我无法理解如何从纹理中获取位掩码(我非常有限的OpenGL知识是主要问题)。似乎BufferImageMask是从AbstractImage实例获得的,而不是从纹理本身获得的。这样做的正确方法是什么?

其次,实现实际碰撞检测算法的不同方法有哪些?我最感兴趣的是,如果有任何方式/变化,因为我到目前为止所阅读的所有内容都是这样的:

Collision Detection Algorithm @ gamedev.net

我只是不想错过任何关键信息,算法本身就很可靠。

提前致谢!

P.S。我在Python 2.7编码,但我宁愿在C中实现实际的像素完美碰撞检测算法,并将其用作扩展。

更新

我设法使用非旋转精灵进行像素完美碰撞检测:

    r1 = collision[entity1].aabb
    r2 = collision[entity2].aabb
    sprite1 = renderable[entity1].sprite
    sprite2 = renderable[entity2].sprite
    ri = r1.intersect(r2)


    offx1, offy1 = int(ri.left - r1.left), int(ri.top - r1.top)
    offx2, offy2 = int(ri.left - r2.left), int(ri.top - r2.top)

    d1 = sprite1.texture.get_image_data().get_data('A', sprite1.texture.width)
    d2 = sprite2.texture.get_image_data().get_data('A', sprite2.texture.width)

    p1 = cast(d1, POINTER(c_ubyte))
    p2 = cast(d2, POINTER(c_ubyte))

    for i in range(0, int(ri.width)):
        for j in range(0, int(ri.height)):
            c1, c2 = 0, 0
            x1 = offx1+i
            y1 = (j+offy1)*sprite1.texture.width
            x2 = offx2+i
            y2 = (offy2+j)*sprite2.texture.width

            if x1 >= 0 and y1 >= 0:
                c1 = p1[x1 + y1]

            if x2 >= 0 and y2 >= 0:
                c2 = p2[x2 + y2]

            if c1>0 and c2 >0:
                pairs.add(pair)
                break

collision和renderable只是与给定实体关联的对象的dicts。该算法是此算法的修改版本:Pyglet pixel perfect collision

虽然这是非常好的(并且速度很快,即使对于像这样的黑客一起编写的代码),它对于旋转的精灵来说是无用的(我需要,除非在不同的角度缓存大约100个不同版本的相同精灵是可行的选择),所以我仍然在寻找解决这个问题的方法。

2 个答案:

答案 0 :(得分:1)

虽然我无法回答关于pyglet,纹理和图像的第一个问题(我在在线API中的Texture中找到了一个名为“get_image_data”的方法;也许可以使用它?), 我可以回答第二个问题。

除了您在链接中提供的方法之外,我至少知道其他两种方法:第一种是使用OpenGL,将图像绘制到缓冲区,并检查它们是否重叠。我不知道这有多快,但我可以想象它很慢。但它确实支持旋转和缩放。

第二个与你给出的有些相似;但是找到并检查两个凸包的交点,而不是找到两个轴对齐的边界框的交点并且仅检查交叉点内部。这样做的一个优点是凸包通常提供更好的配合,另一个优点是凸包可以旋转,而轴对齐的边界框则不能。主要缺点是凸包交叉比轴对齐边界框交叉要复杂得多,因此很难实现。

我写了一个利用第二种方法的库;你可以在PoxelColl找到它。它应该比其他像素完美的碰撞检测库更快,特别是对于旋转和缩放。我不知道你是否可以从Python中使用它;如果您使用Jython,则有一个Scala版本,以及您可能能够在Python中进行绑定的C ++版本,但这可能是相当多的额外工作。因此,如果您不需要缩放或旋转,最好只需实现已经找到的版本,并测试它是否足够快。

答案 1 :(得分:1)

回复更新

以下伪代码可能会起到作用:

...

middleX1 = sprite1.texture.width/2
middleY1 = sprite1.texture.height/2

middleX2 = sprite2.texture.width/2
middleY2 = sprite2.texture.height/2

angle1 = ?   #Radians.
vX11 = -cos(angle1)
vY11 = -sin(angle1)
vX12 = -cos(angle1 + math.pi/2)
vY12 = -sin(angle1 + math.pi/2)

angle2 = ?   #Radians.
vX21 = -cos(angle2)
vY21 = -sin(angle2)
vX22 = -cos(angle2 + math.pi/2)
vY22 = -sin(angle2 + math.pi/2)

for ...
    for ...

        ...

        aX1 = x1 - middleX1
        aY1 = j+offy1 - middleY1
        aX2 = x2 - middleX2
        aY2 = j+offy2 - middleY2

        tX1 = vX11*aX1 + vY11*aY1 + middleX1
        tY1 = vX12*aX1 + vY12*aX1 + middleY1

        tX2 = vX21*aX2 + vY21*aY2 + middleX2
        tY2 = vX22*aX2 + vY22*aX2 + middleY2

        #Use tX* and tY* for indexing. Remember to multiply tY* with width.

        ...

这应该工作,假设角度是时钟方式和弧度,旋转应该发生在每个精灵的中间。基本上,它是一些手工编码的矢量和矩阵数学。一个更整洁,更易维护的解决方案将使用矩阵,但我不知道很多Python,所以我决定避免这种情况。

数学的工作原理是,对于每个图像中的每个点,找到它与中间的相对位置,通过将坐标与沿着旋转的新轴的单位向量相乘来旋转它,最后添加中间后面以获得非相对坐标。