代码之家  ›  专栏  ›  技术社区  ›  Matt Greenhalgh

如何利用时间差实现到达行为?

  •  2
  • Matt Greenhalgh  · 技术社区  · 10 年前

    我正在尝试创建一个具有到达行为的自主代理,它基于Reynolds经典Boids论文,更具体地说,是Dan Shiffman在natureofcode.com上的实现。这两种方法都很容易理解(也很有效),但它们都假定一个固定的标称更新步骤,而不是查询自上次帧更新以来经过的时间,并在Euler积分步骤中应用此时间增量。他们简单地建议:

    //pseudocode
    acceleration.add(force);
    velocity.add(acceleration);
    location.add(velocity);
    

    然而,如果我想包括时间增量,那么各种物理论文建议我应该将其改写为:

    //pseudocode
    delta = timeElpasedSinceLastFrameUpdate();
    acceleration.add(force);
    deltaAcceleration = acceleration.mult(delta);
    velocity.add(deltaAcceleration );
    deltaVelocity = velocity.mult(delta);
    location.add(deltaVelocity);
    

    在我尝试执行转向行为,特别是到达行为之前,这也是可行的。有关方法的参考,请参见此处链接中的示例6.2:

    Nature of Code Steering Behaviours

    有问题的步骤(在伪代码中)是转向力=期望速度;

    然而,当应用时间增量时,当我将加速度乘以增量时,转向力有效地由分数时间增量缓和,因此没有对累积速度施加足够的减速,使物体在时间上停止,我得到了摆动行为。

    我可以通过不将累积力(即加速度)乘以时间增量来“修复”这个问题,但这似乎不正确。

    所以,我的问题是:

    如何生成到达行为的steeringForce以说明可变的增量时间?

    任何帮助都将不胜感激!

    1 回复  |  直到 10 年前
        1
  •  1
  •   pingul    10 年前

    编辑:我错过了你问题的主要部分。有很多方法可以得到你想要的结果,但它们的方法不同。我能想到的最简单的办法可能是跳过 steeringForce 现在,所有这些都在一起,而只是修改速度。

    我们需要这样一个向量

    desired_velocity = velocity + correction
    

    这与您之前所写的相同(除了“steeringForce”):

    correction = desired_velocity - velocity
    

    通过做 velocity + correction 你会立即达到你想要的速度,这通常不是你想要的,因为它会导致一个非常急促的动作。相反,您可以将校正钳制为某个值,从而使运动更平滑:

    len = length(correction)
    corr_length = len > max_correction ? max_correction : len
    correction = correction/len*corr_length
    

    现在你可以了

    velocity += correction
    

    这将导致某种程度的动态运动,而不会产生振荡。


    显式时间步长积分(例如,在您的情况下是正向欧拉积分)通常写为

    new_acceleration = old_acceleration + (delta_t/mass)*force
                                                  ^^ note
    

    同样,

    new_velocity = old_velocity + delta_t*acceleration
    new_position = old_position + delta_t*velocity
    

    acceleration.add(force.mult(delta_t/mass));
    velocity.add(acceleration.mult(delta_t));
    location.add(velocity.mult(delta_t));