将球体移动到平面永远不会预测未来的碰撞

时间:2012-05-15 00:24:03

标签: c++ algorithm physics game-physics

简短版本:

如何正确地将重力添加到物理更新?

详细版本:

我遇到的问题是,移动球体到平面的通用碰撞算法(在下面张贴以供参考)永远不会到达将要检测到未来碰撞的点。我相信这是因为我的物理更新。

我已将其设置为只有在确定将来会发生碰撞时,重力才会应用于对象。因此,在将重力应用于对象之前,首先进行碰撞检查。但是,由于始终假设从未有任何未来的碰撞,因此永远不会应用重力。想象一下具有

值的场景
spherePosition = (0, 5, 0)
sphereVelocity = (2, 0, 0)
sphereRadius = 1
planeOrigin = (0, 0, 0)
planeNormal = (0, 1, 0)

这总是假设球体正在向平面移动parrelel。结果,永远不会施加重力。

我的更新相对简单,例如

mAcceleration = mTotalForces / mMass;
Vector3 translation = (mVelocity * fElapsedTime) + 0.5 * mAcceleration * pow(fElapsedTime, 2);
mVelocity += mAcceleration * fElapsedTime;

所以操作顺序大致是

int collisionResult = checkCollision(sphere, plane);
if(collisionResult == 2)
{
    sphere.applyGravity(); // Just sets "mAcceleration" to (0, -9.81, 0). Which is then removed by my physics update.
}
sphere.update(timeSlice);

说完所有这些,我应该何时在物理更新循环中将重力应用于我的对象以及如何?如果我在碰撞检查之前应用它,那么在碰撞检查期间无关紧要,如果我之后要做,如果将来会发生碰撞,我应该如何调整更新?

碰撞检查参考:

int IntersectMovingSpherePlane(float sphereRadius, const Vector3& sphereCenter, const Vector3& sphereVelocity, const Vector3& planeNormal,
    const Vector3& planeOrigin, float planeD, float &t, Vector3 &q)
{
    // Compute distance of sphere center to plane
    float dist = Vec3Dot(&planeNormal, &sphereCenter) - planeD;
    if (fabs(dist) <= sphereRadius) {
        // The sphere is already overlapping the plane. Set time of
        // intersection to zero and q to sphere center
        t = 0.0f;
        q = sphereCenter;
        return 0;
    } else {
        float denom = Vec3Dot(&planeNormal, &sphereVelocity);
        if (denom * dist >= 0.0f) {
            // No intersection as sphere moving parallel to or away from plane
            return 1;
        } else {
            // Sphere is moving towards the plane

            // Use +r in computations if sphere in front of plane, else -r
            float r = dist > 0.0f ? sphereRadius : -sphereRadius;
            t = (r - dist) / denom;
            q = sphereCenter + t * sphereVelocity - sphereRadius * planeNormal;
            return 2;
        }
    }
}

2 个答案:

答案 0 :(得分:1)

这里最简单的方法是让重力成为mTotalForces的一部分。

更物理化的方法是将重力作为单独的加速度。然后在最后加速度值后添加“mTotalForces / mMass”。

修改

Vector3 translation = (mVelocity * fElapsedTime) + 0.5 * mAcceleration * pow(fElapsedTime, 2);

在这里,你的代码似乎是为了统一运动。但是如果你想要在击中地面后停止球落下,那就不均匀了。

对于这种非均匀运动,通常我们只是细分时间范围并在非常短的时间内进行均匀计算,使运动可以被认为是均匀的,并忽略累积误差。

答案 1 :(得分:1)

向系统添加物理的正确方法是使用迭代过程,称为runge-kutta method(或牛顿,一阶runge-kutta)。

基本上,您的模型将具有质量随时间演变的粒子。你必须“塑造”的唯一事情是每个蜱上施加在每个粒子上的力。用它来计算加速度。

如果世界有重力,你必须说在每个粒子上,加速度是-g(向下指向负值,在这种情况下与质量无关)。

例如,第一个命令runge-kutta是:

newVelocity = oldVelocity + tickAcceleration*dt
newPosition = oldPosition + newVelocity*dt

其中dt是您选择勾选的时间步。

通常,为避免物体通过其他物体,您实际上并未计算新的*。您计算了一个建议的位置:

proposedVelocity = oldVelocity + tickAcceleration*dt
proposedPosition = oldPosition + proposedVelocity*dt

然后计算碰撞条件(您将每个粒子与其他粒子进行测试)。如果粒子碰撞,则应用碰撞方程(例如elastic collision),并相应地更新newPositions / velocity。如果它们没有发生碰撞,则使用建议的*值

更新新*
newVelocity = proposedVelocity
newPosition = proposedPosition

编辑:

在1º近似值上,您使用哪一个碰撞无关紧要。然而,2º近似可能是首先计算建议的速度和位置。然后检查旧位置和新位置之间是否发生碰撞,并计算碰撞时间,我称之为dtCol。然后我们可以将时间步分为两部分:碰撞前,dtCol和碰撞后的dt-dtCol。第一部分是将拟议的数量设置为

proposedVelocityBeforeCollision = oldVelocity + tickAcceleration*dtCol
proposedPositionBeforeCollision = oldPosition + proposedVelocityBeforeCollision*dtCol

此数量正好在碰撞之前。您现在可以使用then然后使用碰撞方程式计算建议的VelocityAfterCollision(proposedPositionAfterCollision = proposedPosition)。

之后,您更新新的*数量:

newVelocity = proposedVelocityAfterCollision + tickAcceleration*(dt-dtCol)
newPosition = proposedPosition + newVelocity*(dt-dtCol)

请注意,此过程不会考虑多次碰撞。

希望这有帮助。