Verlet积分法导致我的物理引擎出现问题

5

我正在构建一个物理引擎,之前使用了一种“伪Verlet”算法,现在想要升级到真正的Verlet算法。于是我找到了一篇文章并开始工作。但是,在我添加了我认为是很好的近似方法后,引擎就不再工作了。有人能帮助我理解我做错了什么吗?

我的主要物理体类的更新、施加力和施加冲量系统:

public void Update(float timestepLength)
{
    if (!this._isStatic)
    {
        Vector2 velocity =  Vector2.Subtract(_position, _lastPosition);
        Vector2 nextPos = _position + (_position - _lastPosition) + _acceleration * (timestepLength * timestepLength);
        _lastPosition = _position;
        _position = nextPos;
        _acceleration = Vector2.Zero;
    }
}

public void ApplyForce(Vector2 accelerationValue)
{
    if (!this._isStatic)
        _acceleration += (accelerationValue) * _mass;
}
public void ApplyImpulse(Vector2 impulse)
{
    if (!this._isStatic)
        _acceleration +=-1 * impulse;
}

编辑: 我已经修复了它,现在它可以像魔法一样工作,但是我有两个关于下面代码的问题:

  • 这个冲量应用代码是否正确?如果不正确,应该怎么改正?
  • 如何更改位置属性,以便设置时保留物体当前的速度?

以下是代码:

public Vector2 Position
{
    get { return _position; }
    set { _position = value;}
}
public void Update(float timestepLength)
{
    if (!this._isStatic)
    {
        Vector2 velocity =  Vector2.Subtract(_position, _lastPosition);
        Vector2 velocityChange = Vector2.Subtract(velocity, Vector2.Subtract(_lastPosition, _twoStepsAgoPosition));
        Vector2 nextPos = _position + (_position - _lastPosition) + _acceleration * (timestepLength * timestepLength);
        _twoStepsAgoPosition = _lastPosition;
        _lastPosition = _position;
        _position = nextPos;
        _acceleration = Vector2.Multiply(velocityChange, timestepLength);
    }
}

public void ApplyForce(Vector2 force)
{
    if (!this._isStatic)
        _lastPosition -= force;
}
public void ApplyImpulse(Vector2 impulse)
{
    if (!this._isStatic)
        _acceleration +=-1 * impulse;
}

为什么不用 _acceleration -= impulse; ? - Peter Lillevold
1个回答

4
作为对他人的参考...你可能在指这篇论文: 高级角色物理学,由创建Hitman的团队制作,是最早基于布娃娃的物理学之一。
总之...他们使用的原始代码是:
 void ParticleSystem::Verlet() {
       for(int i=0; i<NUM_PARTICLES; i++) {
             Vector3& x = m_x[i];
             Vector3 temp = x;
             Vector3& oldx = m_oldx[i];
             Vector3& a = m_a[i];
             x += x-oldx+a*fTimeStep*fTimeStep;
             oldx = temp;
       }
 }

你说的没错,你的代码也是做了类似的事情。

我的模拟器总是出问题,原因之一就是使用了太大的时间步长。此外,在使用这种 Verlet 积分时,请确保您使用的时间步长在游戏中始终保持不变。(例如每秒 30 帧,所以时间步长为 1/30),并且不要波动。如果有波动,您应该使用时间校正的 Verlet 积分来解决这个问题。

编辑:

回答问题2:如果要改变位置(而不改变速度/加速度),只需将位置更新为新位置,然后再额外添加运动的增量(delta即newPosition-oldPosition)到旧位置上,从而相应地更新旧位置。

回答问题1:脉冲是在一段时间内施加在物体上的力。因此,你的解决方案是不正确的。脉冲将是在 X 个时间步长(或帧)中,使用一个固定的力调用 applyForce 函数。


我实际上是以http://www.gamedev.net/reference/programming/features/verlet/为基础的。问题在于,粒子根本不会移动(在线性重力作用下),如果我将“_acceleration = Vector2.Zero;”行替换为_acceleration = Vector2.Multiply(_acceleration, 1 - _linearDrag);它的行为非常异常(粒子开始移动,然后当它们撞到将它们绑定到区域的约束时,它们会猛烈地飞出屏幕,我怀疑它们的值很快被设置为NaN)。 - RCIX
3
这篇引用的游戏开发文章已经移动到这里 - Robert Sköld
Lonesock关于时间校正Verlet积分的文章现在只能在存档中获取:https://web.archive.org/web/20150210025424/lonesock.net/article/verlet.html - c69

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接