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

Moving sphere to plane never predicts a future collision

本文关键字:未来 碰撞 永远 移动 平面      更新时间:2023-10-16

简短版本:

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

详细版本:

我遇到了一个问题,即移动球体到平面的通用碰撞算法(发布在下面以供参考(永远不会达到检测到未来碰撞的点。我相信这是由于我的物理更新方式。

我设置了它,以便只有当确定将来会发生碰撞时,重力才会施加到物体上。因此,在将重力施加到物体之前,首先要进行碰撞检查。但是,由于始终假设永远不会有任何未来的碰撞,因此永远不会施加重力。想象一个值为

spherePosition = (0, 5, 0)
sphereVelocity = (2, 0, 0)
sphereRadius = 1
planeOrigin = (0, 0, 0)
planeNormal = (0, 1, 0)

这始终假设球体正在向平面移动。因此,重力将永远不会被应用。

我的更新相对简单,例如

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;
        }
    }
}

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

一种更物理的方法是使重力成为单独的加速度。然后添加到"mTotalForce/mMass"以获得最终的加速度值。

编辑

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

看来这里的代码是用于匀速运动的。但是如果你想让球在落地后停止掉落,它就不均匀了。

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

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

基本上,您的模型将具有质量随时间演变的粒子。您唯一必须"建模"的是施加在每个刻度上每个粒子上的力。有了它,您可以计算加速度。

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

例如,一阶 runge-kutta 是:

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

其中 dt 是您为即时报价选择的时间步长。

通常,为了避免对象通过其他对象传递,您实际上不会计算 new*。您计算建议的职位:

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

然后计算碰撞条件(你测试每个粒子与其他粒子(。如果粒子发生碰撞,则应用碰撞方程(例如弹性碰撞(,并相应地更新新位置/速度。如果它们没有冲突,则使用建议的*值更新新的*,

newVelocity = proposedVelocity
newPosition = proposedPosition

编辑:

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

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

这个数量正好在碰撞之前。现在,您可以使用 then 来计算建议的 VelocityAfterCollision 和碰撞方程(提议的位置后碰撞 = 提议的位置(。

之后,更新新的*数量:

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

请注意,此过程不会考虑多个 tick 冲突。

希望这有帮助。