Delta timing:
Do...
Check what the current time is in Millisecs()
Subtract it from the time that you got when you checked in the previous loop
Calculate a scale factor based on how much time passed, so for example if you design for 60fps then divide the elapsed time by 16.67 which will give you a number around 1.0, +/- the fluctuation.
Multiply this scale factor by everything in your game to do with movement or change - ie how much you add to object positions, how fast it accelerates, how quickly the anim frames change, etc
Render your graphics based on the new updated positions.
Loop
When the framerate is below 60 the scale factor will be above 1.0 and your objects will `jump` further/faster each loop to compensate.
When the framerate is above 60 the scale factor will shrink below 1.0 and your objects will move/change in smaller steps.
Delta timing focusses on making your code adapt to the changing availability of processing time, based on increases and decreases in things happening on your computer and in the game itself. But because it is adaptive it means you're also at the mercy of circumstances which isn't ideal.
It works well on low-spec computers because if there isn't enough time to do everything at an ideal speed, even the logic, you can still get an impression of constant movement rates for all objects even if you see them jumping a bit. With delta timing the logic itself can be executed less often which is a plus. You ideally want the least amount of processing time for the smoothest framerate. But delta does have a drawback - it doesn't work with physics math. Physics requires that there be an exact linear time step between each frame otherwise this `error` magnifies and things can explode/become unstable. So it partly depends how you do your logic as to whether it's going to work for you.
Fixed Rate Logic:
Decide on a Hertz rate at which your logic will run e.g. every 8 millisecs (about 120 Hz).
Set an accumulator counter to 0 once outside your main loop.
Measure how many milliseconds have passed since the previous time you checked (previous loop).
Add that to the accumulator counter.
If the accumulator counter is >= 8 then do your logic.
If you just did the logic, subtract 8 from the accumulator counter.
Do your graphics rendering once before coming back to check the time again.
Loop
If there it time left after doing 1 logic update and 1 graphics render, it will either go to the logic again or to another graphics update. Logic has higher priority than rendering. One downside here is while the logic is operating the graphics cannot and vice versa. The graphics might block the logic. Ideally you want to do logic in parallel to graphics, like with threads.
Obviously if you happened to have lots of time spare and tried to do a render pass again you'll be rendering the same object positions as last time - which is not only a waste but won't show any visual change. This is where tweening comes in.
To tween you are taking the most recently generated fixed-rate-logic step and you're now adding delta timing to it, ie how much time has passed since the last logic update, converted into a scale factor, multiplied by how far each object moves per logic update, so that when you render the objects will gradually progress toward the position they would be at when the next real logic update happens. You can either do the tweening by taking the logic data and making a copy of it at the same time as doing the multiplication and then rendering based on that, or you can implementing the multiplication tweening into the actual rendering code.
Then when you have lots of extra time spare the framerate can go way up and you will see more detailed/refined movements, and if it drops due to lots of logic needing to be done then at least the objects still move and change at a constant rate.
Fixed rate logic is kind of like being able to get the benefits of delta timing - ie adapt to the changes in processing time - but in a way that makes your code appear to not to have to change very much. It gives you some extra consistency and simplicity.
Having a fixed rate has the drawback that the logic has to consume as much time as it needs regardless of framerate, which overall may consume more processing time than delta timing. If you try to make things move smoothly across a wide range of display Hz rates then you have to consider that a logic Hz or 61fps with a display Hz of 60fps is going to mean that occasionally you will be a whole frame off from where you should be. People get around this by increasing the logic rate to give it more `resolution`, so that for example at 120Hz you won't ever be more than half a frame off, or at 240Hz you wouldn't be more than 1/4 of a frame off, but hey that's still 4 milliseconds. And the higher you put the logic the more processing time it consumes, which is bad if you do lots of logic and automatically has less processing time available for graphics etc on lower-end machines. If your low end machine can run okay with logic at 60hz but the framerate drops to 30 when you put your logic at 120hz then that's a problem.
The solution is to combine fixed rate and delta using tweening, so that you then don't have to run the logic rate higher in a vain attempt to narrow the error gap. Even if your logic was at 1000Hz there may be still 1 millisecond error in where objects should be - although people might not notice. Driving up the logic rate is a very cpu intensive way of trying to achieve better accuracy. Instead you use a lot less cpu time by having a lower logic hz, even 60Hz would be fine, and then tweening/delta to make up for the remaining difference. That gives you the benefits of fixed rate and delta combined - better for the low-end, better for physics, better for consistency.
One drawback of fixed rate is that if you fix the rate to be the same for a number of display hz rates you will get a different `moire` offset effect based on the combination of the hz rates (ie logic against display, and also you have to factor in the graphics update hz rate). That's whay I suggest you try making a different fixed rate based on an exact multiple of the display hz because then you should get no such error effect. Upping the logic hz rate isn't an ideal way to deal with it. But if you do that you lose predictable design-time benefits of a single fixed rate, which is why you'll then need to consider a separate `design hz` which will have to be tweened to the logic hz. At least, that's my theory.
This seems to be pretty much the state of the art so far, at least published on the net. But I think there are some flaws in all these approaches which begs for an entirely new way of doing it. It's kind of like saying, okay delta does some good things and some bad things, so we want to add something else to make it better, so we add fixed rate which does some good things and some bad things, so we add tweening which does some good things and some bad things, etc etc... endlessly searching. If you were to start out with the ideal system right off the bat you wouldn't need to keep adapting and adding stuff to try to correct problems, which only create more problems.
Do...
Check what the current time is in Millisecs()
Subtract it from the time that you got when you checked in the previous loop
Calculate a scale factor based on how much time passed, so for example if you design for 60fps then divide the elapsed time by 16.67 which will give you a number around 1.0, +/- the fluctuation.
Multiply this scale factor by everything in your game to do with movement or change - ie how much you add to object positions, how fast it accelerates, how quickly the anim frames change, etc
Render your graphics based on the new updated positions.
Loop
When the framerate is below 60 the scale factor will be above 1.0 and your objects will `jump` further/faster each loop to compensate.
When the framerate is above 60 the scale factor will shrink below 1.0 and your objects will move/change in smaller steps.
Delta timing focusses on making your code adapt to the changing availability of processing time, based on increases and decreases in things happening on your computer and in the game itself. But because it is adaptive it means you're also at the mercy of circumstances which isn't ideal.
It works well on low-spec computers because if there isn't enough time to do everything at an ideal speed, even the logic, you can still get an impression of constant movement rates for all objects even if you see them jumping a bit. With delta timing the logic itself can be executed less often which is a plus. You ideally want the least amount of processing time for the smoothest framerate. But delta does have a drawback - it doesn't work with physics math. Physics requires that there be an exact linear time step between each frame otherwise this `error` magnifies and things can explode/become unstable. So it partly depends how you do your logic as to whether it's going to work for you.
Fixed Rate Logic:
Decide on a Hertz rate at which your logic will run e.g. every 8 millisecs (about 120 Hz).
Set an accumulator counter to 0 once outside your main loop.
Measure how many milliseconds have passed since the previous time you checked (previous loop).
Add that to the accumulator counter.
If the accumulator counter is >= 8 then do your logic.
If you just did the logic, subtract 8 from the accumulator counter.
Do your graphics rendering once before coming back to check the time again.
Loop
If there it time left after doing 1 logic update and 1 graphics render, it will either go to the logic again or to another graphics update. Logic has higher priority than rendering. One downside here is while the logic is operating the graphics cannot and vice versa. The graphics might block the logic. Ideally you want to do logic in parallel to graphics, like with threads.
Obviously if you happened to have lots of time spare and tried to do a render pass again you'll be rendering the same object positions as last time - which is not only a waste but won't show any visual change. This is where tweening comes in.
To tween you are taking the most recently generated fixed-rate-logic step and you're now adding delta timing to it, ie how much time has passed since the last logic update, converted into a scale factor, multiplied by how far each object moves per logic update, so that when you render the objects will gradually progress toward the position they would be at when the next real logic update happens. You can either do the tweening by taking the logic data and making a copy of it at the same time as doing the multiplication and then rendering based on that, or you can implementing the multiplication tweening into the actual rendering code.
Then when you have lots of extra time spare the framerate can go way up and you will see more detailed/refined movements, and if it drops due to lots of logic needing to be done then at least the objects still move and change at a constant rate.
Fixed rate logic is kind of like being able to get the benefits of delta timing - ie adapt to the changes in processing time - but in a way that makes your code appear to not to have to change very much. It gives you some extra consistency and simplicity.
Having a fixed rate has the drawback that the logic has to consume as much time as it needs regardless of framerate, which overall may consume more processing time than delta timing. If you try to make things move smoothly across a wide range of display Hz rates then you have to consider that a logic Hz or 61fps with a display Hz of 60fps is going to mean that occasionally you will be a whole frame off from where you should be. People get around this by increasing the logic rate to give it more `resolution`, so that for example at 120Hz you won't ever be more than half a frame off, or at 240Hz you wouldn't be more than 1/4 of a frame off, but hey that's still 4 milliseconds. And the higher you put the logic the more processing time it consumes, which is bad if you do lots of logic and automatically has less processing time available for graphics etc on lower-end machines. If your low end machine can run okay with logic at 60hz but the framerate drops to 30 when you put your logic at 120hz then that's a problem.
The solution is to combine fixed rate and delta using tweening, so that you then don't have to run the logic rate higher in a vain attempt to narrow the error gap. Even if your logic was at 1000Hz there may be still 1 millisecond error in where objects should be - although people might not notice. Driving up the logic rate is a very cpu intensive way of trying to achieve better accuracy. Instead you use a lot less cpu time by having a lower logic hz, even 60Hz would be fine, and then tweening/delta to make up for the remaining difference. That gives you the benefits of fixed rate and delta combined - better for the low-end, better for physics, better for consistency.
One drawback of fixed rate is that if you fix the rate to be the same for a number of display hz rates you will get a different `moire` offset effect based on the combination of the hz rates (ie logic against display, and also you have to factor in the graphics update hz rate). That's whay I suggest you try making a different fixed rate based on an exact multiple of the display hz because then you should get no such error effect. Upping the logic hz rate isn't an ideal way to deal with it. But if you do that you lose predictable design-time benefits of a single fixed rate, which is why you'll then need to consider a separate `design hz` which will have to be tweened to the logic hz. At least, that's my theory.
This seems to be pretty much the state of the art so far, at least published on the net. But I think there are some flaws in all these approaches which begs for an entirely new way of doing it. It's kind of like saying, okay delta does some good things and some bad things, so we want to add something else to make it better, so we add fixed rate which does some good things and some bad things, so we add tweening which does some good things and some bad things, etc etc... endlessly searching. If you were to start out with the ideal system right off the bat you wouldn't need to keep adapting and adding stuff to try to correct problems, which only create more problems.