零星碰撞检测

发布于 2024-10-26 03:43:47 字数 2772 浏览 2 评论 0原文

我一直致力于检测游戏中物体之间的碰撞。现在一切都是垂直的,但希望保留其他运动的选择。这是经典的 2D 垂直太空射击游戏。

现在我循环遍历每个对象,检查碰撞:

for(std::list<Object*>::iterator iter = mObjectList.begin(); iter != mObjectList.end();) {
    Object *m = (*iter);
    for(std::list<Object*>::iterator innerIter = ++iter; innerIter != mObjectList.end(); innerIter++ ) {
            Object *s = (*innerIter);

            if(m->getType() == s->getType()) {
                break;
            }

            if(m->checkCollision(s)) {
                m->onCollision(s);
                s->onCollision(m);
            }
        }
    }

这是我检查碰撞的方法:

bool checkCollision(Object *other) {
        float radius = mDiameter / 2.f;
        float theirRadius = other->getDiameter() / 2.f;
        Vector<float> ourMidPoint = getAbsoluteMidPoint();
        Vector<float> theirMidPoint = other->getAbsoluteMidPoint();

        // If the other object is in between our path on the y axis
        if(std::min(getAbsoluteMidPoint().y - radius, getPreviousAbsoluteMidPoint().y - radius) <= theirMidPoint.y &&
            theirMidPoint.y <= std::max(getAbsoluteMidPoint().y + radius, getPreviousAbsoluteMidPoint().y + radius)) {

                // Get the distance between the midpoints on the x axis
                float xd = abs(ourMidPoint.x - theirMidPoint.x);

                // If the distance between the two midpoints
                // is greater than both of their radii together
                // then they are too far away to collide
                if(xd > radius+theirRadius) {
                    return false;
                } else {
                    return true;
                }

        }
        return false;
}

问题是它会随机正确地检测到碰撞,但其他时候根本检测不到它。这不是 if 语句脱离对象循环,因为对象确实具有不同的类型。对象距离屏幕顶部越近,正确检测到碰撞的机会就越大。越靠近屏幕底部,正确检测甚至根本检测不到的机会就越小。然而,这些情况并不总是发生。物体的直径很大(10 和 20),看看这是否是问题所在,但这根本没有多大帮助。

编辑 - 更新代码

bool checkCollision(Object *other) {
    float radius = mDiameter / 2.f;
    float theirRadius = other->getDiameter() / 2.f;
    Vector<float> ourMidPoint = getAbsoluteMidPoint();
    Vector<float> theirMidPoint = other->getAbsoluteMidPoint();

    // Find the distance between the two points from the center of the object
    float a = theirMidPoint.x - ourMidPoint.x;
    float b = theirMidPoint.y - ourMidPoint.y;

    // Find the hypotenues
    double c = (a*a)+(b*b);
    double radii = pow(radius+theirRadius, 2.f);

    // If the distance between the points is less than or equal to the radius
    // then the circles intersect
    if(c <= radii*radii) {
        return true;
    } else { 
        return false;
    }
}

I've been working on detecting collision between to object in my game. Right now everything tavels vertically, but would like to keep the option for other movement open. It's classic 2d vertical space shooter.

Right now I loop through every object, checking for collisions:

for(std::list<Object*>::iterator iter = mObjectList.begin(); iter != mObjectList.end();) {
    Object *m = (*iter);
    for(std::list<Object*>::iterator innerIter = ++iter; innerIter != mObjectList.end(); innerIter++ ) {
            Object *s = (*innerIter);

            if(m->getType() == s->getType()) {
                break;
            }

            if(m->checkCollision(s)) {
                m->onCollision(s);
                s->onCollision(m);
            }
        }
    }

Here is how I check for a collision:

bool checkCollision(Object *other) {
        float radius = mDiameter / 2.f;
        float theirRadius = other->getDiameter() / 2.f;
        Vector<float> ourMidPoint = getAbsoluteMidPoint();
        Vector<float> theirMidPoint = other->getAbsoluteMidPoint();

        // If the other object is in between our path on the y axis
        if(std::min(getAbsoluteMidPoint().y - radius, getPreviousAbsoluteMidPoint().y - radius) <= theirMidPoint.y &&
            theirMidPoint.y <= std::max(getAbsoluteMidPoint().y + radius, getPreviousAbsoluteMidPoint().y + radius)) {

                // Get the distance between the midpoints on the x axis
                float xd = abs(ourMidPoint.x - theirMidPoint.x);

                // If the distance between the two midpoints
                // is greater than both of their radii together
                // then they are too far away to collide
                if(xd > radius+theirRadius) {
                    return false;
                } else {
                    return true;
                }

        }
        return false;
}

The problem is it will randomly detect collisions correctly, but other times does not detect it at all. It's not the if statement breaking away from the object loop because the objects do have different types. The closer the object is to the top of the screen, the better chance it has of collision getting detected correctly. Closer to the bottom of the screen, the less chance it has of getting detected correctly or even at all. However, these situations don't always occur. The diameter for the objects are massive (10 and 20) to see if that was the problem, but it doesn't help much at all.

EDIT - Updated Code

bool checkCollision(Object *other) {
    float radius = mDiameter / 2.f;
    float theirRadius = other->getDiameter() / 2.f;
    Vector<float> ourMidPoint = getAbsoluteMidPoint();
    Vector<float> theirMidPoint = other->getAbsoluteMidPoint();

    // Find the distance between the two points from the center of the object
    float a = theirMidPoint.x - ourMidPoint.x;
    float b = theirMidPoint.y - ourMidPoint.y;

    // Find the hypotenues
    double c = (a*a)+(b*b);
    double radii = pow(radius+theirRadius, 2.f);

    // If the distance between the points is less than or equal to the radius
    // then the circles intersect
    if(c <= radii*radii) {
        return true;
    } else { 
        return false;
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

橘虞初梦 2024-11-02 03:43:47

当两个圆形物体的中心之间的距离足够小时,它们就会发生碰撞。您可以使用以下代码来检查这一点:

double distanceSquared =
    pow(ourMidPoint.x - theirMidPoint.x, 2.0) +
    pow(ourMidPoint.x - theirMidPoint.x, 2.0);
bool haveCollided = (distanceSquared <= pow(radius + theirRadius, 2.0));

为了检查两个时间点之间是否存在碰撞,您可以在时间间隔的开始和结束时检查碰撞;但是,如果物体移动得非常快,碰撞检测可能会失败(我猜您已经遇到过这个问题,即在屏幕底部速度最快的下落物体)。

以下内容可能会使碰撞检测更加可靠(尽管仍然不完美)。假设物体以匀速运动;那么,它们的位置是时间的线性函数:

our_x(t) = our_x0 + our_vx * t;
our_y(t) = our_y0 + our_vy * t;
their_x(t) = their_x0 + their_vx * t;
their_y(t) = their_y0 + their_vy * t;

现在您可以将它们之间的(平方)距离定义为时间的二次函数。找出什么时候它呈现最小值(即它的导数为0);如果该时间属于当前时间间隔,则计算最小值并检查是否发生碰撞。

这必须足以几乎完美地检测碰撞;如果您的应用程序大量处理自由落体物体,您可能需要将运动函数细化为二次函数:

our_x(t) = our_x0 + our_v0x * t;
our_y(t) = our_y0 + our_v0y * t + g/2 * t^2;

Two circular objects collide when the distance between their centers is small enough. You can use the following code to check this:

double distanceSquared =
    pow(ourMidPoint.x - theirMidPoint.x, 2.0) +
    pow(ourMidPoint.x - theirMidPoint.x, 2.0);
bool haveCollided = (distanceSquared <= pow(radius + theirRadius, 2.0));

In order to check whether there was a collision between two points in time, you can check for collision at the start of the time interval and at the end of it; however, if the objects move very fast, the collision detection can fail (i guess you have encountered this problem for falling objects that have the fastest speed at the bottom of the screen).

The following might make the collision detection more reliable (though still not perfect). Suppose the objects move with constant speed; then, their position is a linear function of time:

our_x(t) = our_x0 + our_vx * t;
our_y(t) = our_y0 + our_vy * t;
their_x(t) = their_x0 + their_vx * t;
their_y(t) = their_y0 + their_vy * t;

Now you can define the (squared) distance between them as a quadratic function of time. Find at which time it assumes its minimum value (i.e. its derivative is 0); if this time belongs to current time interval, calculate the minimum value and check it for collision.

This must be enough to detect collisions almost perfectly; if your application works heavily with free-falling objects, you might want to refine the movement functions to be quadratic:

our_x(t) = our_x0 + our_v0x * t;
our_y(t) = our_y0 + our_v0y * t + g/2 * t^2;
夜访吸血鬼 2024-11-02 03:43:47

这个逻辑是错误的:(

if(std::min(getAbsoluteMidPoint().y - radius, getPreviousAbsoluteMidPoint().y - radius) <= theirMidPoint.y &&
        theirMidPoint.y <= std::max(getAbsoluteMidPoint().y + radius, getPreviousAbsoluteMidPoint().y + radius))
{
  // then a collision is possible, check x
}

大括号内的逻辑也是错误的,但这应该产生误报,而不是漏报。)检查在时间间隔内是否发生碰撞可能很棘手;我建议现在检查是否存在碰撞,并首先使其发挥作用。当您检查碰撞时(现在),您无法独立检查 x 和 y,您必须查看对象中心之间的距离。

编辑:

编辑后的代码仍然不太正确。

// Find the hypotenues
double c = (a*a)+(b*b); // actual hypotenuse squared
double radii = pow(radius+theirRadius, 2.f); // critical hypotenuse squared

if(c <= radii*radii) { // now you compare a distance^2 to a distance^4
    return true; // collision
}

它应该是这样的:

double c2 = (a*a)+(b*b); // actual hypotenuse squared
double r2 = pow(radius+theirRadius, 2.f); // critical hypotenuse squared

if(c2 <= r2) {
    return true; // collision

}

或这样的:

double c2 = (a*a)+(b*b); // actual hypotenuse squared
double c = pow(c2, 0.5); // actual hypotenuse
double r = radius + theirRadius; // critical hypotenuse

if(c <= r) {
    return true; // collision

}

This logic is wrong:

if(std::min(getAbsoluteMidPoint().y - radius, getPreviousAbsoluteMidPoint().y - radius) <= theirMidPoint.y &&
        theirMidPoint.y <= std::max(getAbsoluteMidPoint().y + radius, getPreviousAbsoluteMidPoint().y + radius))
{
  // then a collision is possible, check x
}

(The logic inside the braces is wrong too, but that should produce false positives, not false negatives.) Checking whether a collision has occurred during a time interval can be tricky; I'd suggest checking for a collision at the present time, and getting that to work first. When you check for a collision (now) you can't check x and y independently, you must look at the distance between the object centers.

EDIT:

The edited code is still not quite right.

// Find the hypotenues
double c = (a*a)+(b*b); // actual hypotenuse squared
double radii = pow(radius+theirRadius, 2.f); // critical hypotenuse squared

if(c <= radii*radii) { // now you compare a distance^2 to a distance^4
    return true; // collision
}

It should be either this:

double c2 = (a*a)+(b*b); // actual hypotenuse squared
double r2 = pow(radius+theirRadius, 2.f); // critical hypotenuse squared

if(c2 <= r2) {
    return true; // collision

}

or this:

double c2 = (a*a)+(b*b); // actual hypotenuse squared
double c = pow(c2, 0.5); // actual hypotenuse
double r = radius + theirRadius; // critical hypotenuse

if(c <= r) {
    return true; // collision

}
两个我 2024-11-02 03:43:47

您的内部循环需要从 mObjectList.begin() 而不是 iter 开始。

内部循环需要迭代整个列表,否则随着外部循环的进展,您会错过碰撞候选者。

Your inner loop needs to start at mObjectList.begin() instead of iter.

The inner loop needs to iterate over the entire list otherwise you miss collision candidates the further you progress in the outer loop.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文