如何用Rabbyt做像素完美的碰撞检测?

2024-09-24 22:16:57 发布

您现在位置:Python中文网/ 问答频道 /正文

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

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

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

其次,实现实际碰撞检测算法的方法有哪些?我最感兴趣的是是否有任何方法/变化,因为到目前为止,我读到的所有东西都是这样的:

Collision Detection Algorithm @ gamedev.net

我只是尽量不漏掉任何重要的信息,算法本身是可靠的。在

提前谢谢!在

另外,我用Python2.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

碰撞和可渲染只是与给定实体关联的对象的指令。该算法是这个算法的一个改进版本:Pyglet pixel perfect collision

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


Tags: 方法算法gettopwidthleft精灵int
2条回答

更新回复

下面的伪代码可能会起作用:

...

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了解不多,所以我决定避免使用。在

数学运算的原理是,对于每个图像中的每个点,找到其相对于中间的相对位置,通过将坐标与单位矢量相乘,沿着旋转的新轴旋转,最后将中间点相加,得到非相对坐标。在

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

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

第二个与您给出的有点类似;但是不是查找两个轴对齐的边界框的交点并且只检查交点内部,而是找到并检查两个凸面外壳的交点。这样做的一个优点是凸壳通常提供更好的拟合,另一个优点是凸壳可以旋转,而轴对齐的边界框则不能。其主要缺点是凸壳求交比轴对齐包围盒求交复杂得多,因此很难实现。在

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

相关问题 更多 >