matter-js
matter-js copied to clipboard
Timing, frame-rate consistency improvements and features
Features
- improves all round consistency of the engine with respect to timing and frame-rates (delta)
- adds several new velocity and speed functions to
Matter.Body
- adds
updateVelocity
argument toBody.setPosition
etc. for e.g. moving bodies around directly but with imparted velocity - changes body velocity and speed values to be frame-rate independent (via getters and setters)
- changes examples to be frame-rate independent
- removes
correction
fromEngine.update
as it is now built in
Notes
For most purposes, these changes are intended to be backwards compatible.
If you're using the engine at the standard browser 60fps
(16.666ms
delta) then results should be essentially the same as before (or better) but if they are not, please leave a comment and an example here if possible.
If you're using Body.setVelocity
, Body.setAngularVelocity
at something other than 60fps
(16.666ms
delta) then you may need to change your velocity values or remove any code you may have been using that factored in frame-rate to the velocity values, since velocity is now constant with respect to delta as expected.
While body.velocity
and body.speed
remain the same, they are internal and not expected for use going forward, instead you should now use Body.getVelocity
and Body.getSpeed
which are frame-rate independent. Code that previously relied on these properties should still function as before for the time being, but it is strongly suggested to update these as they are not consistent or frame-rate independent, they may be removed in future.
The internal standard base time unit remains as 16.666ms
for the time being, rather than 1sec
as used in SI. If this were to change later, 1ms
would seem the natural choice. For most users this is not usually necessary to be aware of.
All examples have been updated to also be frame-rate independent but using 1sec
as their time unit to be more in line with expectations, they are also using the new updateVelocity
argument in e.g. Body.setPosition
which makes it easier to correctly control bodies.
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 inEngine.update
(more than33.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
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
- Added Body.getSpeed, Body.getVelocity, Body.getAngularVelocity
- Changed all velocity functions to be time independent
- Changed examples to be delta independent
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.
@liabru, any update on how this branch looks to you?
@liabru , any update on this branch? Would you like some help resolving some of the above comments?
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.
@getkey
Are you also doing the interpolation as described in that article?
@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.
@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.
Thanks for the feedback everyone.
So far I've added an initial value for body.deltaTime
. I think this should mean that setting velocity etc. immediately after creation should now work correctly.
I also adjusted Body.applyForce
for timing. I didn't think this was necessary, but it looks like it is.
As for differences in friction between timestep sizes, a small difference is to be expected but maybe there is still a problem to fix on that.
It's definitely advised to always use a fixed timestep e.g. 60hz, then do frame skipping for faster monitors.
Once this work is done, I have some experimental updates to Matter.Runner
that make it fixed by default and do this for you.
@liabru
It's definitely advised to always use a fixed timestep e.g. 60hz
why though? wouldn't you want to sync the refresh rate to the monitor of the user?
I've used this PR (https://github.com/liabru/matter-js/issues/818) in the past, just so I can bypass the 60hz limit. I feels laggy on my screen to run on lower refresh rates.
I'll be checking this PR in the project I'm currently working on!
why though? wouldn't you want to sync the refresh rate to the monitor of the user?
In my opinion there's no "correct" answer to this. You have to choose what you need. If the timestep is synced to a monitor's refresh rate, you'll achieve smooth-looking motion but your simulation will lack a deterministic quality, aka multiples runs of the same physics simulation will yield different results since the series of delta times won't be exactly the same between runs.
On the other hand, if you fix your timestep you get this deterministic quality since the physics simulation will always be a series of steps of an exact amount. But if your physics step is 60hz, and refresh rate is 144hz, your simulation will likely appear choppy since some frames will encompass a physics step and some won't. You can still achieve smooth movement in this fixed-timestep approach through interpolation. This article explains it well.
why though? wouldn't you want to sync the refresh rate to the monitor of the user?
In my opinion there's no "correct" answer to this. You have to choose what you need. If the timestep is synced to a monitor's refresh rate, you'll achieve smooth-looking motion but your simulation will lack a deterministic quality, aka multiples runs of the same physics simulation will yield different results since the series of delta times won't be exactly the same between runs.
On the other hand, if you fix your timestep you get this deterministic quality since the physics simulation will always be a series of steps of an exact amount. But if your physics step is 60hz, and refresh rate is 144hz, your simulation will likely appear choppy since some frames will encompass a physics step and some won't. You can still achieve smooth movement in this fixed-timestep approach through interpolation. This article explains it well.
I got a new trouble. Results different on server and on client, but the game field and code the same. Different is runners realisation. It depends on result?
Server code: let runner = Matter.Runner.create({ isFixed: true }); while (collectedBalls.length < generatedBallsCoords.length) { Matter.Runner.tick(runner, engine); }
Client code: let runner = Matter.Runner.create({ isFixed: true }); Matter.Runner.start(runner, this.engine);
P.S. I need to run physics on server very fast to get result of game...
why though? wouldn't you want to sync the refresh rate to the monitor of the user?
In my opinion there's no "correct" answer to this. You have to choose what you need. If the timestep is synced to a monitor's refresh rate, you'll achieve smooth-looking motion but your simulation will lack a deterministic quality, aka multiples runs of the same physics simulation will yield different results since the series of delta times won't be exactly the same between runs.
On the other hand, if you fix your timestep you get this deterministic quality since the physics simulation will always be a series of steps of an exact amount. But if your physics step is 60hz, and refresh rate is 144hz, your simulation will likely appear choppy since some frames will encompass a physics step and some won't. You can still achieve smooth movement in this fixed-timestep approach through interpolation. This article explains it well.
someone here?...
bump!
bump
So I am pulling my hair out.
After 3 months full time work on my game, I just found out that my game does not work at all on 144Hz monitors. Gravity is much higher, and forces are not correct, breaking my game completely. Which has led me to this thread.
I can see that some of you seem to of managed to come up with work arounds ? Can anyone give me any advice on how to proceed.
I am using the phaser 3 implementation of matter.js.
I am not clear on how to fix the timestep of matter, within the update loop of my phaser 3 game.
Thankyou in advance.
@mturneruk since you're using Phaser, first check with them about their support for 144Hz etc.
In terms of Matter.js, you need to make sure that Matter.Engine.update is always called with the intended fixed timestep at the intended rate, accounting for the user's refresh rate which may be different.
As an example, if your intended update rate is 60Hz the timestep is 1000 / 60 = 16.666ms
and the user's refresh rate is 144Hz 1000 / 144 = 6.944ms
, then you'll need to do some frame skipping (update every 144 / 60 = 2.4
frames) to maintain the same speed when using a single thread.
This is known generally as the game loop, it's worth reading the article Fix your timestep which gives some great examples of techniques to account for this problem.
The built in Matter.Runner
does not currently do this for you because it is primarily intended for debugging and prototyping rather than production games, which require a much more complex game loop to handle a broad range of hardware, so check if Phaser has that feature already (maybe @photonstorm has some ideas to help).
I'd like to implement some of these techniques into Matter.Runner
eventually since higher refresh rates are becoming more common, but for now I consider that a task for game engines to handle.
@driescroons exactly as @wmike1987 says. Essentially it's all about balancing performance with quality for your intended hardware support. Many games run physics as low as 30hz and but render at e.g. 60hz or more by interpolating the motion in the renderer, which generally works really well and allows for physics sub-stepping without the performance hit.
Thankyou so much for your excellent assistance. I now understand the problem from your description.
I have solved my 144Hz problem by setting autoUpdate to false and manually indexing the Matter engine on each step of my game loop which is running at 60hz.
https://phaser.discourse.group/t/question-about-matter-js-and-its-update-loop/4824/9
This branch fixes timing issues with my Gas Retention Simulator - I can now successfully slow down the animation using engine.timing.timeScale
. I would love for these improvements to be in a matter.js release as using this alpha build is a bit awkward.
I tried this branch since I liked the improvements, but the bodies are not going to sleep with this version: https://codepen.io/tylercorleone/pen/yLoKjad
After a couple of seconds, using matter.js: baloon.isSleeping == letters.bodies[0].isSleeping == true using matter.alpha.js none of the body gets sleeping
Could you give please a look? Thanks :)
P.S. I just noticed that setting render.options.showDebug (by console) affects the "master" version but not this one. Maybe these differences are caused by the fact this branch is outdated respect to master?
These changes seem to finally fix velocity being framerate dependent. Any chance to merge this to master soon?
@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 :)
@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?
@liabru Hi, any chance to merge this to master soon?
@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.
@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.
@liabru I don't suppose there's any chance of you merging this into master, is there? :) I really think it could be beneficial!
Look out for these changes in the next release. Thanks again to all those who provided feedback here!