Open simon-kyger opened 6 years ago
TLDR; we can probably get away with setInterval(gameLoop, 1000/tickRate) nowadays (2018)
Explanation of the 16 ms: This came from an era when scheduling a function to be run via setTimeout or setInterval and measuring the time that it actually got invoked was pretty inconsistent. It would sometimes be as late as 16 milliseconds, as if the actual tick rate for the timers was 60 fps. On the other hand setImmediate has a ridiculous tick rate, on the order of 1000s or 10,000s of times per second. So it is possible to step through time in very small steps by calling setImmediate and checking again what time it is, meanwhile stepping through time with setTimeout was chunky and erratic. This code attempted to estimate whether we were in the safe window to use setTimeout to step forward, or if we were coming up too close to when the next tick was going to occur, in which case it would spin setImmediate until we arrived precisely at the time for the physics to tick again. I would suspect this behavior to vary a little by OS and node version, so it may be the case that logic of this kind is still warranted in some cases. To test the timer behavior make a loop with setInterval, and on each tick console.log the time (or a delta) to see if the timers are behaving nicely. I did a quick test on node 8.9.1 in windows and setInterval was usually good to the millisecond (sometimes off by 1 ms).
I added my test code to the readme file https://github.com/timetocode/node-game-loop if you want to use it to measure your timers, or use it as a physics loop.
I am still trying to refine a tick rate (serverside) for a game I'm working on, but these tick rates are ultimately how often the server is going to pump out physics updates to the client.
What I'm failing to understand is how often I should send these up, as well as how to decouple the framerate of the game from the physics. This has become a huge challenge for me.
If I want my framerate to be 60fps for clients, don't I have to be pushing the updates out 60 times a second?
The reason I say the above is because the physics on the server is calculating whether or not the player has moved or it hasn't. Then the draw() itself on the client is just drawing them at whatever position they may be on the client, thus in order not to get frameskipping (or rerendering of the same position multiple times), the client is going to need to get those updates no?
The server can send out a game state snapshot at 20 frames per second, and then the client can interpolate between these snapshots at its own frame rate. This gets complicated for advanced networking features, but just sticking to the topic of entity interpolation the idea is to make the client wait until it has 2 or more snapshots from the server, and then draw every object in the snapshots at positions that show the object moving from its position in snapshotA to its position in snapshotB. I recommend this article https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking ; specifically the part about entity interpolation. This one is good too, http://www.gabrielgambetta.com/client-server-game-architecture.html (the whole series is solid, and it has one part about entity interp).
I am well aware of both articles. It is one thing to theorize, and another to put theory to practice.
Here is repo I am currently working on, and I know I don't have this right, because the framerate of my game is based upon the update loop of the server, which can't be the right way of handling this...
In your loop you have this line:
if (Date.now() - previousTick < tickLengthMs - 16) {
What is the hardcoded 16 for?
I'm trying really hard to understand the serverside physics loops and this looks like a really good example, I am just confused by this one little aspect of your code. Thanks.