0

精简版:

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

详细版本:

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

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

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

2 回答 2

1

将物理添加到系统中的正确方法是使用迭代过程,称为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

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

newVelocity = proposedVelocity
newPosition = proposedPosition

编辑:

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

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

这个数量正好在碰撞之前。您现在可以使用 then 到 then 来使用碰撞方程 (proposedPositionAfterCollision =providedPosition) 计算提出的VelocityAfterCollision。

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

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

请注意,此过程不考虑一次以上的滴答碰撞。

希望这可以帮助。

于 2012-05-15T08:32:29.283 回答
1

可能这里最简单的方法是让重力成为mTotalForces.

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

编辑

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

看来您的代码用于匀速运动。但是如果你想让一个球在落地后停止下落,它是不均匀的。

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

于 2012-05-15T06:30:09.127 回答