Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Timing, frame-rate consistency improvements and features #777

Merged
merged 52 commits into from
Feb 11, 2023
Merged

Conversation

liabru
Copy link
Owner

@liabru liabru commented Sep 1, 2019

Features

  • improves general consistency of results between different timesteps based on 60hz as a baseline
  • changes Body.setAngularVelocity and Body.setVelocity functions to be timestep independent
  • adds timestep independent Body.setSpeed, Body.setAngularSpeed, Body.getSpeed, Body.getVelocity, Body.getAngularVelocity
  • adds optional updateVelocity argument to Body.setPosition, Body.setAngle, Body.translate, Body.rotate
  • removes correction from Engine.update as it is now built in
  • changes examples to be timestep independent

Notes

When using a fixed timestep of 60hz (~16.666ms engine delta) results should look similar to before, as this was taken as the baseline.

If you're using a non-fixed timestep or one other than 60hz (~16.666ms) results should now become more similar to the 60hz baseline, therefore you may need to adjust e.g. body and constraint properties.

Since Body.setAngularVelocity and Body.setVelocity are now timestep independent, you may need to adjust code you may have been using that factored in the timestep.

For timestep independence, the Matter.Body speed and velocity related get / set functions now relate to a fixed time unit rather than timestep, currently set as 1000/60 for easier backwards compatibility at the baseline 60hz.

Note that Body.applyForce naturally still remains timestep dependent as before, see the updated Body.applyForce docs for details.

While the properties body.velocity and body.speed (and angular versions) still exist they are not typically recommended for user code, in most cases you should switch to the new Body.getVelocity and Body.getSpeed as they are timestep independent.

Examples have been updated to be timestep independent, as well as using the new functions.

Requesting reviewers, testers and comments

If anybody has any time to help me test this update out further and feedback any issues, it would be greatly appreciated:

  • try experiment with the standard examples in different environments (browsers, devices)
    • consider friction, air friction, restitution, sleeping, collisions, constraints
  • compare these to using the previous version of Matter.js (e.g. see the demo), results should be very similar or better
  • try this branch with your code base (e.g. use the alpha build on this branch)
  • try using different delta values in Engine.update (more than 33.333ms won't be stable)
  • try using engine.timing.timeScale (as another way of modifying delta)
  • try out the new updateVelocity, body velocity and speed setters / getters

Highlighted changes

  • Added readonly body.deltaTime
  • Added delta property to engine update event
  • Added delta argument to various internal functions
  • Changed timeScale argument to use delta instead on various internal functions
  • Fixed issues when using an engine update delta of 0
  • Improved time independence for friction, air friction, restitution, sleeping, collisions, constraints
  • Removed optional correction argument from Engine.update
  • Removed correction and timeScale from Body.update and Matter.Runner
  • Added updateVelocity argument to Body.setPosition, Body.setAngle, Body.translate, Body.rotate
  • Added Body.setSpeed, Body.setAngularSpeed, Body.getSpeed, Body.getVelocity, Body.getAngularVelocity
  • Changed Body.setAngularVelocity and Body.setVelocity functions to be timestep independent
  • Changed examples to be delta independent

Added delta property to engine update event
Added delta argument to various internal functions
Changed timeScale argument to use delta instead on various internal functions
Fixed issues when using an engine update delta of 0
Improved time independence for friction, air friction, restitution, sleeping, collisions, constraints
Removed optional correction argument from Engine.update
Removed correction and timeScale from Body.update and Matter.Runner
…y.translate, Body.rotate

Added Body.setSpeed, Body.setAngularSpeed
Added Body.getSpeed, Body.getVelocity, Body.getAngularVelocity
Changed all velocity functions to be time independent
@wmike1987
Copy link

wmike1987 commented Oct 24, 2019

I just bought a 144 hz monitor so I'll be testing this branch asap.

Edit: At first glance, simulations now appear much more consistent between 144hz and 60hz for me. With previous versions, the physics varied quite a bit. Will continue to use this branch to see how it behaves and will post updates here.

@wmike1987
Copy link

@liabru, any update on how this branch looks to you?

Copy link

@getkey getkey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is amazing! I received some complaints from users on a 144hz monitor that my game was broken, and this branch fixes every issue.

One thing though, applyForce doesn't take into account frame rate, so I am multiplying the force I want by (1000/60)*currentDelta. In a way, it's kinda nice because it allows me to set anything I want as the currentDelta (I'm using a similar method as you here). But it might not be super intuitive that you have to do that.

I need to do some more testing but I'm seriously considering using this branch on my live game.

@@ -10,6 +10,7 @@ module.exports = Common;

(function() {

Common._timeUnit = 1000 / 60;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be great if this property dropped the _. There are some cases where I use it to make Body.applyForce framerate insensitive.

Example from my code:

// deltaAdjustment is Common._timeUnit divided by the smallest delta over the last 30 frames
// (I'm using something similar as your Runner.tick here https://github.com/liabru/matter-js/blob/82bb41535b09e02dd76a20d5955fc7b4102487d6/src/core/Runner.js#L132)
const recoilFactor = -(body.mass * 0.09) * deltaAdjustment;
const recoil = Vector.mult(norm, recoilFactor);

// recoil
Body.applyForce(playerControlledBody, playerControlledBody.position, recoil);

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason is that it's an internal constant so your code shouldn't rely on it. Body.applyForce now accounts correctly so I guess you won't need it?

src/body/Body.js Outdated
body.positionPrev.x = body.position.x - velocity.x * timeScale;
body.positionPrev.y = body.position.y - velocity.y * timeScale;
body.velocity.x = velocity.x * timeScale;
body.velocity.y = velocity.y * timeScale;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes you made to the way velocity work broke something in my game. It's a game where you have a character that can shoot bodies while moving. I want the speed of the bullet to be the addition of the speed of the character and a force at which the bullet is thrown. I was doing this by:

  • making a bullet body
  • setting the velocity of the bullet to the velocity of the character
  • applying a force to "throw" the bullet

Obviously, as you warned here, I shouldn't have done it this way, so it's okay that it broke (I'm doing it properly now 😛 ). But I thought you might want to know. 🙂

*/
Engine._bodiesUpdate = function(bodies, deltaTime, timeScale, correction, worldBounds) {
Engine._bodiesUpdate = function(bodies, delta) {
for (var i = 0; i < bodies.length; i++) {
var body = bodies[i];

if (body.isStatic || body.isSleeping)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The deltaTime of the static and sleeping bodies do not get set. So if you are doing getVelocity on them you'll get NaN.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I think this should be fixed now with the initial value for body.deltaTime?

dvdbrink added a commit to madmultimedia/phaser that referenced this pull request Nov 15, 2019
dvdbrink added a commit to madmultimedia/phaser that referenced this pull request Nov 19, 2019
@wmike1987
Copy link

@liabru , any update on this branch? Would you like some help resolving some of the above comments?

@getkey
Copy link

getkey commented Dec 24, 2019

I am now updating my physics at a fixed timestep so I am no longer using this but FYI there seems to be something wrong with the frictions. Bodies were sliding much faster on a body with friction set to 0 at 144Hz than at 60Hz.

@wmike1987
Copy link

@getkey

Are you also doing the interpolation as described in that article?

@wmike1987
Copy link

@liabru , similar to @getkey , I'm also seeing significant differences from friction-air on bodies between 144 and 60 hz. 144hz appears to apply friction more than 60hz.

@getkey
Copy link

getkey commented Jan 31, 2020

@wmike1987 yes, my implementation is strongly inspired by https://github.com/IceCreamYou/MainLoop.js

I wish I could have used MainLoop.js directly but it didn't fit nicely in my ECS-based architecture.

* master: (32 commits)
  fix lint
  update dependencies
  Revert "Merge branch 'pr/526'"
  Revert "Merge branch 'pr/527'"
  changed alpha build configuration
  add window global, stub require and handle bad values in test tools
  added overlap metric to test tools
  fix path to build in test worker
  implemented threaded comparison testing
  fixed plugins in compare
  Added build comparison tools and tests
  Added config and test files to lint
  Set loose build version on dev server
  Added watch content base to dev server
  added timing to engine snapshot
  updated readme
  added tag push to release task
  updated readme
  removed yuidocjs dev dependency
  removed unused gulp release tasks
  ...

# Conflicts:
#	src/collision/Resolver.js
#	src/core/Engine.js
@hubertgrzeskowiak
Copy link

These changes seem to finally fix velocity being framerate dependent. Any chance to merge this to master soon?

@photonstorm
Copy link

@liabru I see you're still working on this, from time to time - are there any plans to merge it into master? I think a lot of people are waiting for this :)

@liabru
Copy link
Owner Author

liabru commented Sep 2, 2022

@photonstorm that's the plan, a major goal was to get constraint stiffness and friction to be consistent across deltas, which I think is now working nicely. Did you try it out at all?

@happy-unicorn
Copy link

happy-unicorn commented Sep 13, 2022

@liabru Hi, any chance to merge this to master soon?

@photonstorm
Copy link

@photonstorm that's the plan, a major goal was to get constraint stiffness and friction to be consistent across deltas, which I think is now working nicely. Did you try it out at all?

Not yet, but I'm working on the 3.60 release of Phaser right now, so would love to have this merged in! I'll try it out next week with our set of examples - if those all work without issue, that would be a really healthy sign.

@sebsowter
Copy link

@photonstorm that's the plan, a major goal was to get constraint stiffness and friction to be consistent across deltas, which I think is now working nicely. Did you try it out at all?

Not yet, but I'm working on the 3.60 release of Phaser right now, so would love to have this merged in! I'll try it out next week with our set of examples - if those all work without issue, that would be a really healthy sign.

Looking forward to the merge! I have a few games that are crying out for this update.

@photonstorm
Copy link

@liabru I don't suppose there's any chance of you merging this into master, is there? :) I really think it could be beneficial!

@liabru liabru merged commit b81d6e6 into master Feb 11, 2023
@liabru
Copy link
Owner Author

liabru commented Feb 11, 2023

Look out for these changes in the next release. Thanks again to all those who provided feedback here!

@liabru liabru mentioned this pull request Feb 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.