I have a simulation which uses the Unity Rigidbody and some custom simple Euler integration (or similar models). It is required that all physics models can be driven (“steered”) by the same acceleration provider. This means I need to apply all forces in a predictable way.
For example, I move a Transform manually with a constant acceleration of 1 unit/second^2 over 10 seconds towards the z-axis in the Update loop. I can predict it will arrive at position z = 50. I can do the same thing by using the Rigidbody.AddForce method, if I apply the same acceleration at every FixedUpdate step.
// Move Transform via simple Euler integration
private void Update()
{
velocity += acceleration * Time.deltaTime;
transform.position += velocity * Time.deltaTime;
}
// Move Rigidbody the same distance
private void FixedUpdate()
{
// Will be multiplied by fixedDeltaTime for me.
rigidbody.AddForce(acceleration, ForceMode.Acceleration);
}
This works fine so far. However, I would actually like to be able to change the update step of the steering. For now, it would be perfect, if I could calculate and apply my acceleration variable in Update(). I can simply apply it to my custom models because they respect a variable timestep. But Unity’s Rigidbody force must be applied in FixedUpdate(), so I need to accumulate the steering acceleration and only apply it every physics step. How do I do this correctly? At the moment, my final traveled distance is too short, also I can’t be sure if, when and how many times Update vs FixedUpdate is called.
// Rigidbody physics model class
Vector3 acceleration;
float accumulatedTime;
public void Apply(Vector3 steeringForce) // Called from regular Update
{
// While waiting for next physics step, accumulate per-frame force.
acceleration += steeringForce;
accumulatedTime += Time.deltaTime;
}
private void FixedUpdate()
{
// This also jitters, so I was reverting back to accumulating force without the timestep
// and applying it simply as ForceMode.Force
rigidbody.AddForce(steeringForce * accumulatedTime, ForceMode.Impulse);
acceleration = Vector.Zero;
// We have applied the accumulated force and reset it for next round.
}
Maybe this approach is already wrong, but in theory it should make sense, if Update is called multiple times in between FixedUpdate. Because my steeringForce is calculated per-frame, I need to add it up until I can apply it. In practice however, it seems that I keep missing frames or something, because the rigidbody moves slower and travels only to around 46 units (it should land right on 50 as it does when applying the force the normal way in FixedUpdate).
Where’s my mistake or how can I solve this?
PS: Once the steering in Update works, I would like to upgrade to a system, where the steering is only calculated every 100ms or so. In this case much less frequent than the regular FixedUpdate loop. Is it possible to accomodate for both cases, a slower and a faster updated acceleration variable for the Rigidbody?