Skip to main content

Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines
(1 edit)
Best is to decouple the logic framerate from graphics framerate.

I do have separate timing for each of these (or i do in win32, but removed in web to try to play with emscripten), is there a way in emscripten to let the renderer do whatever but get my gaurenteed 60hz tick? Maybe I should just do a while(true) and give up on the emscripten loop.

(2 edits)

What I do is this:

  • Keep target timestamp, when next tick is supposed to occur. Sleep aiming for this target or next graphics frame, whichever comes earlier.
  • Whenever code wakes up - assume any amount of time may have passed
  • Check against target: If only 1 tick has passed, set new target = (old_target(!) + tickrate). This is the FPS-independent steady logical tickrate.
  • If more than 1 tick has passed, advance by only 1 tick (avoid frame skip - effectively pause game if tab is not focused). Then set new target=now. Obviously it’s also an option to spin through some or all missed ticks, might be desired if tickrate is not RTS-tier low…
  • Now draw, in case the time has come for a new graphics frame
let the renderer do whatever but get my gaurenteed 60hz tick?

You’re completely at the browser’s mercy. Usually there is e.g. a hard-limit of one wakeup per second on tabs that are not being shown. If a tab is open, the invocations are still wildly inaccurate from a gamedev pov. emscripten_sleep(10) will wake up after 8-30 ms, depending on browser and os.

setTimeout is already inaccurate, and emscripten_sleep is just a very bad/clever hack that unravels the current stack, stores it and schedules a task with setTimeout which will restore that stack and resume its execution.

My hack against this is to call emscripten_sleep with only 75% of the time I actually want to be delayed, and to never even call it with values lower than 8 ms, but rather just skip those 8 ms and move on directly.

Sdlcoven is now my only emscripten-based web version. The others are based on winit, which is a pain to build around, but nicely communicates "browser requests a draw" (browser always Vsyncs btw.) versus "you wake up because of an event / because you asked to be woken up." It makes for very fluid rendering and gameplay, if it is wired up correctly.

Ok wow that's pretty interesting and informative, thx. So after writing my last post I decided to try requesting 999 (as big a number as possible) and then doing my WaitTime (raylib function) call at the start of the game loop. I only wait to try to get as close to my 60hz tick as possible. Then the rest of my game loop is the same as win32: if it's time to do logic, do it, if it's time to render, do it (basically what you wrote). This gets me pretty close in Firefox (~58) and exactly 60 in Chrome. Just deployed that change so I'm curious what your Firefox gets since you've been the big outlier.

raylib WaitTime seems to call nanosleep?? No idea what that's calling in web, I'm not compiling emscripten with ASYNC so I don't think I have access to emscripten_sleep without that compiler switch.

(+1)

Now shows a 59 in the corner. Very mellow experience.

raylib WaitTime seems to call nanosleep?? No idea what that's calling in web, I'm not compiling emscripten with ASYNC

Ooh, very interesting. The libc sleep functions in emscripten should do nothing without -sASYNCIFY. Maybe it is already busy-spinning.