liabru / matter-js

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

Updating body not accounting for delta time when only velocity is set? #637

Closed wmike1987 closed 1 year ago

wmike1987 commented 6 years ago

I'm seeing differing body speeds (visually, not body.speed) when framerates differ. Movement is meant be framerate independent but it doesn't appear to be with respect to only velocity. I think this line is the issue (in Body.update()).

body.velocity.x = (velocityPrevX * frictionAir * correction) + (body.force.x / body.mass) * deltaTimeSquared;

body.velocity.y = (velocityPrevY * frictionAir * correction) + (body.force.y / body.mass) * deltaTimeSquared;

The logic here will disregard delta time if force == 0 and will thus update the body with the same velocity if our frame rate is 60 or 30. Am I missing something, or using the engine wrong?

Mike

wmike1987 commented 6 years ago

Any comment about this one?

MDFL64 commented 5 years ago

I noticed this as well and I'm having a pretty hard time wrapping my head around it. I'm probably misunderstanding the math, but it seems like velocity isn't distance / time but is more like distance / timestep. I'm sure this makes sense to the engine but it makes dealing with and changing velocities super confusing.

liabru commented 5 years ago

It does seem strange but this actually falls out of the math from position Verlet integration:

 xi+1 = xi + (xi - xi-1) + a * dt * dt

Check out this derivation, note that this assumes a constant time step. To account for a changing time step the author introduces a time correction scheme, which is what is implemented in the engine, though it's only intended for handling short dips since it's not that accurate. Hopefully that helps.

wmike1987 commented 5 years ago

Oh ok, thank you so much. I see how this all works now, indeed matter.js is implementing the time correction scheme proposed in that article, however calling setVelocity() on a body updates the body's previous position, which essentially sets the velocity to units per delta-time-of-last-frame rather than normalizing it to something like units per second, or whatever. So really I need to scale the initial velocity I give my bodies to mean the same thing regardless of frame rate.

Qbyte248 commented 3 years ago

It does seem strange but this actually falls out of the math from position Verlet integration:

 xi+1 = xi + (xi - xi-1) + a * dt * dt

Check out this derivation, note that this assumes a constant time step. To account for a changing time step the author introduces a time correction scheme, which is what is implemented in the engine, though it's only intended for handling short dips since it's not that accurate. Hopefully that helps.

I think the formula is correct (see also Wikipedia Velocity integration).

According to my understanding, your code in Body.update calculates essentially

v[i] = (x[i] - x[i-1])
v[i+1] = v[i] + a * dt * dt
x[i+1] = x[i] + v[i+1]

However, velocities should be defined as the difference of current and previous position divided by the time step to make it "physically correct" i.e.

v[i] = (x[i] - x[i-1]) / dt
v[i+1] = v[i] + a * dt
x[i+1] = x[i] + v[i+1] * dt

This eliminates the strange dt * dt and the result of the position update is the same as before, but the velocity update is different/correct.

liabru commented 3 years ago

@Qbyte248 it looks like you're assuming a fixed timestep there, which as it stands the engine does not assume, so it can't be simplified or rearranged that way in this case (you'll need a dt and a dt-1 term).

The latest discussion on this topic is found in the PR #777 which intends to normalise velocities among other things, please do try that out and help review if you're interested.

liabru commented 1 year ago

Closing as I think this was covered by #777 which is now released in 0.19.0 (see the release notes). Thanks again.