【问题标题】:A fast collision detection between a circle and a sprite in pixel perfect像素完美的圆和精灵之间的快速碰撞检测
【发布时间】:2014-08-18 16:07:30
【问题描述】:

我一直在考虑一些快速而出色的像素 - 圆形和任何精灵之间的完美碰撞检测。我需要获得 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;
}

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

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

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

【问题讨论】:

  • “精灵”是指“矩形”,对吧?
  • 我不太明白你的问题。精灵不必是矩形(如主题中,问题是要找到像素完美的碰撞点),但它的碰撞区域(边界框)是矩形。
  • @Savail 添加一些图像,因为似乎只有您知道您在写什么。特别有用的是与它发生碰撞的精灵和圆圈的布局图像。它们是通用的/共面的/垂直的吗?还有更多……如果没有实际的详细算法描述,就没有人能够检查您的代码(而且为什么我的代码不起作用也是题外话)
  • 我在帖子中添加了更新
  • 我没有在心里解析你的代码,但是从精灵中我看到你试图检测边界碰撞检测。将圆形或对角线(边界)线放入光栅可能会导致两条交叉线不相互重叠 - 像这样:1 2

标签: java android geometry collision-detection


【解决方案1】:

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

如果您想要另一种可能更有效的准确方法,请查找分离轴定理。

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

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

这里:

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

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

【讨论】:

    【解决方案2】:

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


    我没有在心里解析您的代码,但是从图像中我看到您尝试检测边界冲突。将圆形或对角线(边界)线放入栅格可能会导致两条交叉线不相互重叠 - 像这样:

    1 2

    2 1

    其中 1 将是第 1 行,而 2 将是第 2 行。


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

    x_

    xx

    x 代表你的线的像素,_ 是一个空的光栅位。

    【讨论】:

      猜你喜欢
      • 2023-04-02
      • 2013-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-07
      • 2010-10-16
      • 2012-04-28
      • 2011-08-20
      相关资源
      最近更新 更多