【问题标题】:simple 2d collision problem简单的二维碰撞问题
【发布时间】:2011-01-05 14:55:09
【问题描述】:

我想找出静态球和运动球之间发生碰撞的时间,但我提出的算法有时无法检测到碰撞,而运动球会穿过静态球。运动的球受重力影响,静止的则不受。

这是我的碰撞检测代码:

GLfloat whenSpheresCollide(const sphere2d &firstSphere, const sphere2d &secondSphere)
{
    Vector2f relativePosition = subtractVectors(firstSphere.vPosition, secondSphere.vPosition);
    Vector2f relativeVelocity = subtractVectors(firstSphere.vVelocity, secondSphere.vVelocity);

    GLfloat radiusSum = firstSphere.radius + secondSphere.radius;

    //We'll find the time when objects collide if a collision takes place

    //r(t) = P[0] + t * V[0]
    //
    //d^2(t) = P[0]^2 + 2 * t * P[0] * V[0] + t^2 * V[0]^2
    //
    //d^2(t) = V[0]^2 * t^2 + 2t( P[0] . V[0] ) + P[0]^2
    //
    //d(t) = R
    //
    //d(t)^2 = R^2
    //
    //V[0]^2 * t^2 + 2t( P[0] . V[0] ) + P[0]^2 - R^2 = 0
    //
    //delta = ( P[0] . V[0] )^2 - V[0]^2 * (P[0]^2 - R^2)
    //
    //  We are interested in the lowest t:
    //
    //t = ( -( P[0] . V[0] ) - sqrt(delta) ) / V[0]^2
    //

    GLfloat equationDelta = squaref( dotProduct(relativePosition, relativeVelocity) ) - squarev( relativeVelocity ) * ( squarev( relativePosition ) - squaref(radiusSum)  );

    if (equationDelta >= 0.0f)
    {
        GLfloat collisionTime = ( - dotProduct(relativePosition, relativeVelocity) - sqrtf(equationDelta) ) / squarev(relativeVelocity);

        if (collisionTime >= 0.0f && collisionTime <= 1.0f / GAME_FPS)
        {
            return collisionTime;
        }
    }

    return -1.0f;
}

这里是调用碰撞检测的更新函数:

void GamePhysicsManager::updateBallPhysics()
{
    //
    //Update velocity
    vVelocity->y -= constG / GAME_FPS;  //v = a * t = g * 1 sec / (updates per second)

    shouldApplyForcesToBall = TRUE;

    vPosition->x += vVelocity->x / GAME_FPS;
    vPosition->y += vVelocity->y / GAME_FPS;

    if ( distanceBetweenVectors( *pBall->getPositionVector(), *pBasket->getPositionVector() ) <= pBasket->getRadius() + vectorLength(*vVelocity) / GAME_FPS )
    {
        //Ball sphere
        sphere2d ballSphere;
        ballSphere.radius = pBall->getRadius();
        ballSphere.mass = 1.0f;
        ballSphere.vPosition = *( pBall->getPositionVector() );
        ballSphere.vVelocity = *( pBall->getVelocityVector() );


        sphere2d ringSphereRight;
        ringSphereRight.radius = 0.05f;
        ringSphereRight.mass = -1.0f;
        ringSphereRight.vPosition = *( pBasket->getPositionVector() );
        //ringSphereRight.vPosition.x += pBasket->getRadius();
        ringSphereRight.vPosition.x += (pBasket->getRadius() - ringSphereRight.radius);
        ringSphereRight.vVelocity = zeroVector();


        GLfloat collisionTime = whenSpheresCollide(ballSphere, ringSphereRight);

        if ( collisionTime >= 0.0f )
        {
            DebugLog("collision");
            respondToCollision(&ballSphere, &ringSphereRight, collisionTime, pBall->getRestitution() * 0.75f );
        }

        //
        //Implement selection of the results that are first to collide collision

        vVelocity->x = ballSphere.vVelocity.x;
        vVelocity->y = ballSphere.vVelocity.y;

        vPosition->x = ballSphere.vPosition.x;
        vPosition->y = ballSphere.vPosition.y;
    }

为什么没有 100% 的情况下检测到碰撞?它仅在 70% 的病例中被发现。 谢谢。

更新:当我将 FPS 从 30 更改为 10 时,问题似乎得到了解决。FPS 如何影响我的碰撞检测?

【问题讨论】:

  • 那么在这种情况下尝试将 FPS 设置得非常高并逐行调试程序,仔细观察变量值如何与您认为的值一致。
  • 你能提供更多关于respondToCollision函数的细节吗?具体来说,是否递归检测与第三方的进一步碰撞?如何处理帧中的多次碰撞?

标签: c++ collision-detection computational-geometry


【解决方案1】:
delta = ( P[0] . V[0] )^2 - V[0]^2 * (P[0]^2 - R^2)

那不应该是 delta = b2 - 4 ac 吗?


[编辑]哦,我明白了,你把 4 排除在外了。在这种情况下,您确定要同时考虑 t 的两种解决方案吗?

t = ( -( P[0] . V[0] ) - sqrt(delta) ) / V[0]^2

t = ( -( P[0] . V[0] ) + sqrt(delta) ) / V[0]^2

【讨论】:

  • 我正在使用一个替代公式,当 x 之前的系数为偶数时可以使用该公式。
  • 我只考虑一种解决方案 - 时间更短。我对更大的时间不感兴趣,因为它会在对象之间的距离第二次为 0 时发生(碰撞本身发生的时间更短)。
  • 这很可能是由于舍入错误 - 程序预计碰撞将在下一帧发生,但由于轻微的舍入误差,到下一帧碰撞应该已经发生。尝试将您的floats 替换为doubles,或者提前几帧发生冲突(例如,将collisionTime &lt;= 1.0f / GAME_FPS 替换为collisionTime &lt;= 1.9f / GAME_FPS
  • 如果用 double 替换 float 可以解决问题,那么您的算法是错误的!我同意 BlueRaja 的第二个说法,看看你是如何处理碰撞时间的,并确保你没有采取太大的步骤。
  • “碰撞会在这一帧发生吗”近似值也可能给出不正确的结果,因为它没有考虑从一帧到下一帧的速度变化:因此,如果速度是恒定的,它会工作(减去舍入误差),现在第 2 帧的方式可能预测碰撞将发生在第 3 帧,而第 3 帧会说碰撞发生在第 2 帧!
【解决方案2】:

球体有多大,它们移动的速度有多快?一个球体可以在一帧中“跳过”第二个球体(即,它的速度矢量是否比它的宽度长?)。

按照这些思路,如果您在此处删除上限会发生什么:

if (collisionTime >= 0.0f && collisionTime <= 1.0f / GAME_FPS)
{
    return collisionTime;
}

如果球体移动太快,可能是您的算法检测到发生在一帧以上之前的碰撞..(?)

【讨论】:

  • 球体的大小大致相同。通常,这个上限定义了碰撞是否会在下一帧中发生。如果是,则报告冲突。
猜你喜欢
  • 2011-06-27
  • 2011-10-22
  • 1970-01-01
  • 2012-03-27
  • 1970-01-01
  • 2023-03-18
  • 1970-01-01
  • 2011-05-06
  • 1970-01-01
相关资源
最近更新 更多