liabru / matter-js

a 2D rigid body physics engine for the web ▲● ■
MIT License
16.59k stars 1.96k forks source link

understanding delta in Engine.update #148

Closed evanthebouncy closed 1 year ago

evanthebouncy commented 9 years ago

While attempting to simulate a box dropping over 5 seconds, I tried to use different time granularity by varying the "delta" argument to the update function. The code is as follows:

function simulate_no_render(delta) {
  // create world
  var boxA = Bodies.rectangle(400, 0, 80, 80);                             
  World.add(engine.world, [boxA]);        
  var total_time = 5000
  var num_step = total_time / delta     
  for (var i = 0; i < num_step; i++ ) {
    Engine.update(engine, delta)
  }
  console.log(engine.world.bodies[0].position)              
}   

I then called this function with different deltas, 2, 5, 10, and 20. Here are the results:

  Object {x: 400, y: 960.4000000004701}
  Object {x: 400, y: 2252.5106848838022}
  Object {x: 400, y: 4016.504778211938}
  Object {x: 400, y: 6360.991724002313}

I am sure I missed something, because the y position should be similar as it's the same setting of a box being dropped over 5 seconds, the different delta should introduce some small error, i.e. bigger values of delta would cause a bigger error due to inaccuracies, but it shouldn't be this drastic. Any clarification would be great!

If this difference is indeed caused by the different delta values, what is the delta used in Runner.run?

After reading the docs it seems the default delta value is 1000 / 60.

And it seems that changing the delta value actually changes the physics of the world, i.e. the simulation is dependent on the delta value, in that both the force and velocity are measured in units time that is the delta: a setVelocity of 10 in a delta of 10ms is twice as fast as a setVelocity of 10 in a delta of 20ms

matthewtuck commented 8 years ago

I'm seeing this too, I got much higher velocities with higher deltas, whether or not the correction is used. I am still trying to understand the relevant equations in Body.update, but it seems to me this might be related to:

matthewtuck commented 8 years ago

@liabru I suspect this may require an API change as the alternative TCV equations seem to use previous delta instead of correction. And I saw a release-candidate tag on another ticket, so I just wanted to make this more explicit if there's an API freeze coming.

liabru commented 8 years ago

Thanks for the info @matthewtuck. I think there might be a bug here but I need to do more testing. I don't think this particular issue is due to using TCV, but you're right that there could also be unrelated issues with that.

If a fix turns out to be needed, I don't think it will require any API changes, just fixes inside existing methods. Either way, since the project is still pre-1.0.0 API changes are okay if they are absolutely necessary (by release-candidate I mean that a particular feature branch is ready to be merged into the next release unless issues are found).

matthewtuck commented 8 years ago

I created a full test case that I could run and play with the friction. This showed that this problem occurs even without air friction, but seems to be bigger with more friction.

I changed the equations to some on stack overflow and such (without various things you'd want to keep such as time correction, time scaling, air friction or angular velocity), and got this test case working, but I'm not exactly sure where it's currently going wrong.

To me, this suggests two problems, one with the general algorithm and one with the air friction.

It's a bit difficult to compare and contrast the air friction case as the only approach I found to velocity-dependent force was here where drag is implemented as a force, and you're percentage adjusting the velocity instead.

liabru commented 8 years ago

Thanks @matthewtuck I'll check it out soon. One thing to be aware of is that the correction only applies at all if you have less than the target FPS (the default is 60). In other words if you are getting 60fps in the demos then then the equations are equivalent to regular Verlet. Also if you don't pass a value for correction when updating manually, then it will be disabled too.

My guess the problem is that delta is not correctly accounted for in the way air friction is applied. Strange that you still see it with it disabled though.

matthewtuck commented 8 years ago

@liabru Yes I'm aware that there would be no correction in the case of the test case above. That's deliberate as it is designed to be minimal.

However, I just don't understand the algorithms well enough yet, so I don't see the connection between your algorithm and many of the others I've read, and many of the others I've read with each other too.

So I can't really determine if the linked claims of incorrectness of a TCV algorithm would only apply when there is correction, or in all cases. It's possible that the problem is somehow related to the TCV algorithm but not the actual correction, or equally that it's an unrelated issue. If the latter, there could well be three issues.

liabru commented 8 years ago

Well like I said, the TCV falls back to regular Verlet when there is no change in timestep ('correction'): From the article:

Original Verlet: xi+1 = xi + (xi - xi-1) + a * dt * dt

Time-Corrected Verlet: xi+1 = xi + (xi - xi-1) * (dti / dti-1) + a * dti * dti

The term in bold is referred to as correction in the engine. When using Matter.Runner at full 60fps then that term is (dti / dti-1) === 16.666 / 16.666 === 1. If you're updating with your own runner and don't pass a value for correction then it is also 1. So you can see how it becomes equivalent to regular Verlet.

While TCV may still be flawed, I don't think it's the source of the problem you're seeing.

The major difference in the engine is the extra air friction term, which is a factor of velocity. I hoped this would remain constant with regards to timestep, but will need to check.

matthewtuck commented 8 years ago

I'd actually only just noticed that you were using standard verlet instead of velocity verlet. I had previously got accurate results with a VV algorithm. This made me wonder if it was due to the non-self-starting nature of standard verlet, and sure enough, when I looked into the equations, Wikipedia suggests a different formula for the first step which has half the acceleration term. Using this the frictionless results were exactly the same regardless of delta.

Once this was done I tried replacing the frictionAir formula of 1 - body.frictionAir (I removed the time scaling for testing purposes) with Math.pow(1 - body.frictionAir, deltaTime / 20) to achieve delta-independent friction and it was a lot more accurate. Not entirely delta-independent (and I doubt it can be since airFriction isn't part of the verlet algorithm derivation), but much better.

flukeout commented 6 years ago

Just to chime in: I've noticed that when I update the physics simulation to run twice as fast (8ms versus 16ms updates) that frictionAir seems to have twice the effect on the objects. I had to divide my old frictionAir value by two to get the same behaviour as before, which seems to mirror what is happening here.

PS: I'm running it the simulation at greater than 60 FPS to mitigate glitches, etc but only updating what I'm displaying at 60 FPSish via requestAnimationFrame

liabru commented 6 years ago

Thanks for the info guys, marking as bug.

oli414 commented 6 years ago

What is the status of this issue? My game is using the Matter.js physics engine and I'm experiencing a lot of issues with the physics on mobile devices (where the framerate fluctuates a lot). Most appear to be caused by the friction and air friction being dependant on a steady 60 FPS framerate. I'd like to know if this is still the case or if I'm doing something wrong myself.

sundayz commented 5 years ago

I'm trying to run the simulation slower than 16ms, but a bigger delta time makes the objects move slower. When using correction in the world update (calculated as delta / lastDelta) things move MUCH faster than they should. This happens on an object with friction set to zero.

liabru commented 5 years ago

I've been working on this!

See my pull request #777 which should hopefully resolve these issues around timing and frame-rates. Those interested, if you have any time it would be great if you could experiment with that update in your code and report any issues or success, thanks.

liabru commented 1 year ago

Closing as I think this one is resolved by #777.