零星碰撞检测

时间:2011-03-24 01:44:06

标签: c++ physics collision-detection

我一直在努力检测游戏中对象之间的碰撞。现在所有东西都是垂直的,但是希望保持其他运动的选择。这是经典的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;
    }
}

3 个答案:

答案 0 :(得分:3)

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

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;

答案 1 :(得分:2)

这种逻辑是错误的:

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

}

答案 2 :(得分:-1)

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

内循环需要迭代整个列表,否则你会在外循环中进一步推进碰撞候选。