How to update more than 60 times/second?

Hi there !

My question is general to game development in Javascript.

Do you know any way to call an update loop ( no render ) at a fixed rate, up to 120 times / seconds ?

I’m doing a custom physics engine for my game. Currently it’s updated inside the render loop :

function loop() {
 
   delta = clock.getDelta();

   updateGameLogic( delta );

   renderer.render( scene, camera );

   requestAnimationFrame( loop );

};

So the update rate is bound to the render rate, around 50 - 60 / second.
While it works fine with slow games, collision problems start to appear when objects get a little bit fast.
One of these problems is that a fast object can entirely traverse an obstacle, like on the following drawing :

I understand that engine’s vendors generally offer an option to reduce the timestep between updates, ideally somewhere around 120 update/seconds, which is still fine for the CPU and fix most of these issues.

But I don’t know any way to reliably and steadily call a function every 8 milliseconds in JS.
requestAnimationFrame() seems to automatically manage the frame rate and stabilises at 60 FPS.
a delay-free recursive function is called too often and stucks the call stack.
setInterval() and setTimeout() are often delayed too much if there is things to do in the call stack, even when I auto-correct the timeout :

function gameLoop() {

    delta  = gameLoopClock.getDelta() ;

    setTimeout( ()=> {

        updateGameLogic( delta );

        gameLoop();

    }, Math.max( 0, 8 - ( delta * 1000 ) ) );

    // delta is often > 0.008, so a perfect correction is not possible.

};

I struggled a bit and now I’m wondering if it’s possible at all in our most beloved single-threaded language… Should I do that in a worker ?

1 Like

The only way I know of is by updating your monitor’s refresh rate to 120Hz. RequestAnimationFrame respects your monitor’s refresh rate, so if you modify your system settings, then the browser will adjust accordingly. Although it might be hard to get all your users to want to do that.

An issue I can foresee with increasing the framerate to avoid teleporting through obstacles is that you will always find a faster velocity that will have the same problem. For instance, if 10km/h gives you that problem at 60FPS, then 20km/h will give you the same problem at 120FPS.

A possible solution to this problem would be to multiply velocity vector * timeDelta to see how much you’ve traveled since the last frame. If there’s an obstacle between those two points, calculate collision, otherwise update position as usual. This approach would be framerate-agnostic (to a certain degree).

3 Likes

Thanks for you answer @marquizzo

This is smart but not an option for me… And I think web games in general don’t benefit from enough user engagement for that.

An issue I can foresee with increasing the framerate to avoid teleporting through obstacles is that you will always find a faster velocity that will have the same problem. For instance, if 10km/h gives you that problem at 60FPS, then 20km/h will give you the same problem at 120FPS.

You’re right, although in my game I don’t have to worry about unexpected speeds. To be more precise, this is a platformer, and most of the collisions to detect is between the player and the environment. At 60FPS I have no problem, but even though I did my best to improve performances, old smartphones can have occasional drops to 30FPS. At this rate my player can go through the ground after a jump, or traverse a wall if running fast. The only workaround I found is not to care about the loop’s delta. So in the rare case a device is too slow, then the movements will be accordingly slow and the player cannot traverse anything. It’s not great for user experience though.

A possible solution to this problem would be to multiply velocity vector * timeDelta to see how much you’ve traveled since the last frame. If there’s an obstacle between those two points, calculate collision, otherwise update position as usual. This approach would be framerate-agnostic (to a certain degree).

This looks like an option to consider, although there would be quite a lot of refactoring.

If setInterval() doesn’t work for you in the main thread, then you could use a similar worker approach to physics engines. Have it run at your required rate and then call updates to your main app.

With that said, I believe marquizzo is right - this issue should be solved through some internal logic, rather than increasing the framerate.

One primitive option I can offer at the moment is you could try to break down the required collision detection into X steps, and in your loop, perform the check X times, each with the passed time factor divided by X.

So for example Instead of performing the collision detection once for the travelled distance of 3 meters, divide it into 3 steps, check collision for 1m, then 2m, then 3m etc. This will increase the frequency of your detection in a similar fashion to increasing the update frames, but may be a little more performant, since if you find an obstancle in the first step, you can actively break the "loop" and perform the required action.

This is a popular problem (mostly with “My bullets are too fast and go through a wall!!!”) and you can find many other (and probably better) solutions online :slight_smile:

3 Likes

@DolphinIQ thanks for the thought, I like a lot this idea of “update logic 2X by render frame” because it’s easy to implement with my current code, I will give this a shot first.

1 Like

@felixmariotto No problem, hope it helps :slight_smile:

btw. is this gonna be the long awaited sequel Temple of Doom 2 ? :eyes:

2 Likes

:smile: :smile: Thanks for remembering it, this is nice !

Actually no it’s for a new concept, a full 3D platformer. It’s a Swiss girl who must climb a mountain, find edelweiss and reach the peak. Some screenshots of the last proto :

6 Likes

@DolphinIQ Many many thanks for your tip, it solved my issue in 4 lines :

delta = clock.getDelta();

ticks =  Math.round( delta / ( 1 / 120 ) );

for ( let i = 0 ; i < ticks ; i++ ) {

    updateGameLogic( delta / ticks );

};
4 Likes

Wow, that looks beautiful! :heart_eyes:
I guess you are gonna provide users of this forum with an early demo to play, right? :wink:

2 Likes

@looeee Thanks ! Honestly it’s still a mess now, but in a few weeks yes, I need feedback :grin:

2 Likes

Indeed, very pretty! :hibiscus:
Looks like a worthy successor of the legendary Temple of Doom! :skull::space_invader: (unfortunately this forum doesn’t seem to have a Cursed Floating Pharaoh Head Emoji )

1 Like

No, but we do have hieroglyphs at least.

𓁖 𓁗 𓁜𓁟 𓁠 𓁡 𓁢 𓁣 𓁤 𓁥 𓁦 𓁳 𓁴 𓁵 𓃒 𓃓 𓃔 𓃕 𓃘 𓃙 𓃚 𓃛 𓃜 𓃝 𓃞 𓃟 𓃠 𓃡 𓃢 𓃣 𓃤 𓃧 𓃨 𓃩 𓃪 𓃫 𓃬 𓃭 𓃮 𓃯 𓃰 𓃱 𓃲 𓃳 𓃴 𓃵 𓃶 𓃷 𓃸 𓃹 𓃺 𓃻 𓃼 𓃽 𓃾 𓄄 𓄅 𓄆 𓄇 𓄈 𓄉 𓄊 𓆲

3 Likes

Oh thank god. I would never want to be in such an outdated forum, that doesn’t even have a basic hieroglyph support :alien:
This truly has to be best discourse ever! :partying_face:
Thanks, @looeee :smiley:

1 Like

So this is what they blow unicode up with each time :joy:

This is what everyone needs :+1:

4 Likes

Dammit I thought this was a boy with long hair XD

1 Like

Yes, some of the hieroglyphs are very useful - 𓂺 𓂸