像素中圆与精灵之间的快速碰撞检测

时间:2014-08-09 11:41:17

标签: java android geometry collision-detection

我一直在思考一些快速而精彩的像素 - 圆与任何精灵之间的完美碰撞检测。我需要获得2点碰撞,以便稍后能够计算出它们的正常矢量。我设法提出了一些解决方案,但是在我的游戏中进行了更多的缩放,这次碰撞越不准确和不准确......似乎我在下面发布的代码是好的和正确的因为我已经检查了它几次,并花了几天时间一次又一次地阅读......我还在视觉上检查了碰撞面罩和碰撞区域在下面的代码中计算得非常好,所以问题肯定不会在那里但是在这里方法

所以我猜这里的问题是浮点运算中的数据丢失,除非有人发现这个方法存在缺陷?

但是如果问题确实存在数据的浮动丢失,那么你会建议在像素完美的圆形和任何其他精灵之间找到2点碰撞的其他解决方案?我真的很喜欢我的解决方案,因为它相对较快

int xOffset1 = (int)colRectLeft;  // left boundary of the collision area for the first sprite
int xOffset2 = (int)colCircleLeft; // left boundary of the collision area for the circle sprite
int yOffset1 = (int)colRectBottom; // bottom boundary of the collision area for the first sprite
int yOffset2 = (int)colCircleBottom; // bottom boundary of the collision area for the circle sprite
int width = (int)(colCircleRight - colCircleLeft); //width of the collision area - same for both sprites
int height = (int)(colCircleTop - colCircleBottom); // height of the collision area same for both sprites

// Pixel-perfect COLLISION DETECTION between circle and a sprite
// my custom vector classes - nothing special
Math2D.Vector_2 colRightPoint = new Math2D.Vector_2(-1, -1); // The right point of collision lying on the circle's circumference
Math2D.Vector_2 colLeftPoint = new Math2D.Vector_2(-1, -1); // the left point of collision lying on the circle's circumference
boolean colRightFound = false;
boolean colLeftFound = false;

// I'm going through y in the circle's area of collision
for (float y = yOffset2; y < yOffset2 + height; y += 1)
{
    // from equation: (x-Sx)^2 + (y-Sy)^2 = r^2
    // x1/2 = (+-)sqrt(r^2 - (y - Sy)^2) + Sx
    //(Sx, Sy) is (circle's radius, circle's radius) becouse I want the points on the circle's circumference to have positive coordinates
    float x1 =  (float) (Math.sqrt(radius*radius - (y - radius)*(y - radius)) + radius); // the right pixel on the circumference
    float x2 =  (float) (-x1 + 2*radius); // the left pixel on the circumference

    //first I check if the calculated x is inside of the previously calculated area of collision for both circle's area and a sprite's area
    if (x1 >= xOffset2 &&
        x1 <= xOffset2 + width &&
        xOffset1 + x1 - xOffset2 < rectFrameW &&
        yOffset1 + (int)y-yOffset2 < rectFrameH &&
        yOffset1 + (int)y-yOffset2 > 0 &&
        xOffset1 + x1 - xOffset2 > 0)
    {
        //I don't have to check if the point on the circle's circumference is opaque becouse it's always so just check if the same point translated to sprite's area of collision is opaque
        boolean opaqueRectPixel = go.gameData.images.get(go.pic_nr)
            .collision_mask[(int)((yOffset1 + (int)y-yOffset2)*rectFrameW +
                                  (xOffset1 + x1 - xOffset2))];

        if(opaqueRectPixel)
        {
            if(!colRightFound)
            {
                colRightPoint.x = (xOffset1 + x1 - xOffset2);
                colRightPoint.y = (yOffset1 + (int)y - yOffset2);
                colRightFound = true;
            }
            else if(!colLeftFound)
            {
                colLeftPoint.x = (xOffset1 + x1 - xOffset2);
                colLeftPoint.y = (yOffset1 + (int)y - yOffset2);
            }
        }
    }

    //the same logic for the left point on the circle's circumference
    if (x2 >= xOffset2 &&
        x2 <= xOffset2 + width &&
        xOffset1 + x2 - xOffset2 < rectFrameW &&
        yOffset1 + (int)y-yOffset2 < rectFrameH &&
        yOffset1 + (int)y-yOffset2 > 0 &&
        xOffset1 + x2 - xOffset2 > 0)
    {
        boolean opaqueRectPixel = go.gameData.images.get(go.pic_nr)
            .collision_mask[(int)((yOffset1 + (int)y-yOffset2)*rectFrameW +
                                  (xOffset1 + x2 - xOffset2))];

        if(opaqueRectPixel)
        {
            if(!colLeftFound)
            {
                colLeftPoint.x = (xOffset1 + x2 - xOffset2);
                colLeftPoint.y = (yOffset1 + (int)y - yOffset2);
                colLeftFound = true;
            }
            else if(!colRightFound)
            {
                colRightPoint.x = (xOffset1 + x2 - xOffset2);
                colRightPoint.y = (yOffset1 + (int)y - yOffset2);
            }
        }
    }

    // if both points are already found, finish
    if(colLeftFound && colRightFound)
        break;
}

编辑:实际上,我在这种方法中所做的是找到圆与精灵之间的交点

编辑:好的,我上传图片来更好地描述我的算法。我真的尽力解释它,但如果还有什么遗失的,请告诉我!

Collision masks of a cricle and a sprite

enter image description here

另外,如果你不想检查我的代码,我会接受任何其他好的解决方案来找到圆圈和像素完美的任何精灵之间的交叉点:( ......呃,我总是这样遇到碰撞问题......

2 个答案:

答案 0 :(得分:0)

如果您绝对想要(或需要)像素完美,那么您的解决方案看起来很不错。 在测试像素完美检测之前,不要忘记首先进行矩形到矩形的碰撞,以避免不必要的处理。

如果您想要更高效的其他准确方法,请查找分离轴定理。

您可以在此处找到有关它的更多信息:

http://rocketmandevelopment.com/blog/separation-of-axis-theorem-for-collision-detection/

在这里:

http://www.metanetsoftware.com/technique/tutorialA.html

最后一个有很好的互动解释和演示。享受:)

答案 1 :(得分:0)

...因为我无法在评论中显示光栅:


我没有在心理上解析你的代码,但是从我看到你尝试检测边界线碰撞的图像。将圆形或对角线(边框)线放入栅格可能会导致两条交叉线不相互叠加的情况 - 如下所示:

1 2

2 1

其中1表示第1行,2表示第2行。


但是我仍然喜欢检查边框线和矩形预检查的想法。如果您要通过精灵渲染一系列栅格证明闭合线坐标,您可以相互检查它们。这也可以通过边界线分割(例如北,东,西和南或更精细的颗粒 - 我想有一个最佳)来丰富。检查数据集中的对角线证明 - 闭合线必须代表如下:

x _

x x

其中x表示线条的像素,_是空的光栅座位。