-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Timestep doesn't take refresh rate into account #702
Comments
Same issue occurring for me and others |
I've been trying to find out how to fix this as well. Running a game, using MatterJS physics, on 2 different monitors (144hz and 60hz) is like putting on fast forward. |
I think the issue is that while So I'll mark this as a bug, thanks for the report. In the meantime I think the easiest fix is to use As for detecting the refresh rate automatically, well that might actually be a little tricky, but it would probably take some testing using |
A proper solution would be to run in a fixed timestep. Keep in mind that frame rate can be dynamic, i.e. change at any time during single game. |
I had this issue with my users a while back and I fixed it by altering the deltaMin and deltaMax so that it didn't assume 60 fps. I then had one other issue regarding monitor refresh rate and differing performance which may be worth mentioning here: setting velocity via setVelocity() currently does not normalize the velocity parameter. It currently sets the velocity to units-per-delta-time-of-last-frame, which differs with varying refresh rates. See #637 for the issue I opened for that one. |
@wmike1987 how did you go about fixing deltaMin and deltaMax so they do not assume 60 fps? I'm also interested in how you fixed the velocity issue. Edit: Fellow googlers, I limited my fps for monitors with higher refresh rates like so below. I feel like this might be the best solution for now. This also makes it so we do not have to do any weird velocity calculations, because we'll only update the engine on specific intervals while the renderer keeps running. You can check an example of this from a game I made for Ludum Dare 45: @liabru is this a correct implementation? First, I declare the engine, renderer and runner.
followed by a couple of variables that i'll be using to calculate delta (differentiating time in between frames gets rendered) and only let the engine update (using runner's tick method) when it is above the required interval.
I then run my renderer and call a custom update method.
In my update method I call requestAnimationFrame, passing the function as a parameter. When it gets called by requestAnimationFrame, it gets passed a time variable, a total time since first called requestAnimationFrame, I calculate delta, and only call the tick method on my renderer, when it is above the locked the interval. 1000 ms / 60 fps.
|
@hellos3b @driescroons , |
PR #1254 includes updates relating to this issue, see details in that branch if you wish to try it out. |
Since PR #1254 has been open for a while I decided to write my own loop, it's a fixed time step (so you won't lose determinism) that updates up to 144 times per second and has the same perceived speed on different refresh rates. let isRunning = true;
let lastUpdate = performance.now();
const fixedDelta = 1000 / 144;
const runnerFunc = () => {
const now = performance.now();
while (lastUpdate < now) {
Matter.Engine.update(engine, fixedDelta);
lastUpdate += fixedDelta;
}
if (isRunning) {
requestAnimationFrame(runnerFunc);
}
}
requestAnimationFrame(runnerFunc); |
Hey @davidreis97, thanks for sharing this! Would you mind breaking down this code to help us understand how this solves issues associated with different monitor framerates? Thank you! |
@epurban I asked GPT and the response was good enough: The given code snippet addresses the issue of different monitor framerates by decoupling the physics simulation update rate from the frame rendering rate. Here’s how it solves the problem:
In summary, this method allows for a stable and consistent physics simulation that is not affected by varying refresh rates across different monitors. This is particularly useful in gaming or any real-time interactive applications where consistent physics behavior is essential for a fair and uniform user experience. |
@davidreis97 there is a problem with this code if my goal is to run physics steps less frequent than frame update. In your case, physics step will always run at least once for every frame. The following loop will catch up regardless whether physics delta or frame delta is higher: const physicsDelta = 1000 / 144;
let lastUpdate = performance.now();
const runnerFunc = () => {
while (lastUpdate + physicsDelta < performance.now()) {
lastUpdate += physicsDelta;
// physics there
Matter.Engine.update(engine, physicsDelta);
}
// drawing there
}
requestAnimationFrame(runnerFunc); |
I have implemented the last and "best" solution ( which he has mentioned) in my work project where I am creating Plinko.
|
Setup
Observe
The demo runs twice as fast on the 160hz monitor than it does on the 60hz monitor
GIF
I recorded it on my phone to show the difference in simulation speed
https://gfycat.com/HugeAmusingChameleon
I believe this is because of an assumption that
requestAnimationFrame
target FPS is 60, where as on a higher refresh rate RAF can be called at 140fpsThe text was updated successfully, but these errors were encountered: