Ah, gotcha. Now it makes more sense and is exactly what I'm hoping to shoot for, frame rate wise. I'm still super early in my engine build, so I'm at a point where I can still make changes like this and not have to re-factor too much.
Whoops? Sorry, didn't mean to diss Python. I've never done anything with it, but now I know.
Here's my take:
The ideal framework is to update state once per frame, render, wait for v-sync, repeat. Which is awesome when you know the power of the machine you're running on, and that you *know* your render will never take too long, (resulting in dropped frames). Problem is, that's not usually very realistic.
Taking the time difference between this and the previous frame plays havoc with physics and the feel of your game. Whether the game runs slowly or quickly. Weirdness lies this way.
The best framework I've seen is keyframed state update with interpolation for render. This lets you decouple the frequency of state update from render frequency.
The update of things like position of enemies or anything that changes over time, the
state update, is separated from the
render update. The state update can run zero, once, or many times per frame. Interpolation and Render is done once.
First decide how many frames per second your state update will run at, from this you calculate your fixed timestep. Lower the timestep, the better collisions you'll get as things can't travel so far per update. You can pick anything, but 60Hz is common. Depends on the game.
At the start of each game loop you figure out how many state updates should have been generated since the start of the game, starting at 2, (explained below).
NOTE Real time is not used to update the internal state, just to work out how many state updates should have been generated at this point in real time.
You then step the state update until you are at that number. Each step generates a new frame of state for the game, and stores the previous frame's state. Let's call the previous frame State Frame 0 and the new frame State Frame 1. So, every time you step, you copy State Frame 1 to State Frame 0, and generate a new State Frame 1.
State Frame 1 is always at or up to one time step ahead of real time. You need this because once you've generated past the current time you can interpolate the state for the actual real time. You figure out how far you are between the last two frames, generate a number between 0.0 and 1.0, and use that number to lerp all the state from state frame 0 to state frame 1. You then render things at their interpolated positions. Simple!
Your physics is always updated with a fixed timestep, meaning your controls feel the same. Your display can run vsynced, non-vsynced, slow, fast, whatever. You still play the exact same internal game. It'll feel great at 500fps, and the physics won't do anything weird! Same if it's displaying slowly, 20fps won't mean 1/20th second update for the physics, which may let fast moving objects start to pass through walls, and other such crap. It's still look like shit, because 20fps is.. shit!
One thing I have noticed with this is that you feel like you have more control at higher frame rates, even though you don't. Weird effect, but we tested it years ago at work. The game could update its state at 20Hz and feel fine as long as the display ran at 60Hz. We picked 60Hz state frequency for physics issues though.
NOTE This doesn't require threading. You can do this all on one thread. Start frame, run state updates as required, interpolate, render, optionally wait for v-sync.
MASSIVE NOTE Settle on a state timestep before fine tuning any physics handling/controls. Your magic numbers will only feel right for one time step. Doubling the timestep and halving your settings WILL NOT WORK. It'll be subtley off, and
feel wrong.
A few of the games I've worked on, (Colin McRae Rally, SEGA Rally), worked like this, and Unity does too:
Code:
... the execution order for any given script:
All Awake calls
All Start Calls
while (stepping towards variable delta time)
[INDENT]All FixedUpdate functions
Physics simulation
OnEnter/Exit/Stay trigger functions
OnEnter/Exit/Stay collision functions[/INDENT]
Rigidbody interpolation applies transform.position and rotation
OnMouseDown/OnMouseUp etc. events
All Update functions
Animations are advanced, blended and applied to transform
All LateUpdate functions
Rendering
https://docs.unity3d.com/Documentation/Manual/ExecutionOrder.html
So yeah, what Mental Atrophy said, but with more words
Hope that helps someone!