Hello. After reading for a while, I've realized that buffered inputs with time-stamp is the way to go for a fixed time step simulation that requires input logging.
So, in a fixed-time-step game simulation we need to simulate FIXED_TIME_STEP of game time if the current frame time is bigger than that, so consume FIXED_TIME_STEP of the current time — in order to the game logic catch up the real-time at any situation using a fixed time. Like the above loop:
m_tRenderTime.Update();
UINT64 ui64CurMicros = m_tRenderTime.CurMicros();
while (ui64CurMicros - m_tLogicTime.CurTime() > FIXED_TIME_STEP) {
NextState();
if ( !m_smStateMachine.Tick(this) ) { return false; }
m_tLogicTime.UpdateBy( FIXED_TIME_STEP );
}
Since now everything looks correct.
From what we know, polling input events from the OS at the beginning of the frame allows you the simplicity, and for some people, may be the state of the art. But in my case I'm using a separated thread — the main thread — to do the job, so it stays all the time doing that:
while (m_bPollEvents) {
::WaitMessage();
while( ::PeekMessage( &mMsg, m_hWnd, 0U, 0U, PM_REMOVE ) ) {
::DispatchMessage(&mMsg);
}
}
...and when a event happens for instance, this is what I do:
case WM_KEYDOWN: {
m_kbKeyboardBuffer.OnKeyDown( WindowKeyTableLookUp(_wParam) );
break;
}
(...)
The keyboard buffer basically has a critical section, local timer, etc. When the key gets pressed — like the one above — I insert the keyboard input event in a vector of keyboard events. This allows me a direct fast keyboard keys event look-up without needing to insert key data for each event (which is a waste of time, memory, and cache usage).
A key event is the most basic structure in the world. It has a event that can be pressed or released, and the time-stamp.
(...)
std::vector<KB_KEY_EVENT> m_keKeyEvents[KB_TOTAL_KEYS];
With a one-day research, some guys limited the KB_KEY_EVENT to a maximum size, in order to avoid dynamic allocation, but since we don't want to miss any event, it is probably better to sacrifice that.
The keyboard is updated by its buffer. I do that for every type of device in my game — touch, mouse, gamepad — in order to make things operating in a single responsability only.
So, the problem is:
I need to consume FIXED_TIME_STEP of input each logical update. For such operation, I'd need to update the keyboard by that buffer on each logical update, because the player can press a button before the frame starts for instance and release during the game logic update. If I'd to just update the keyboard each frame, the player could press a button in the start of the frame and the button would be interpreted as it was being held during the entire frame. For instance:
Frame took 500ms;
FIXED_TIME_STEP is 100ms;
Total logic updates in one frame is 5;
Input occurred in the beginning of the frame;
Input was released in the 2th update;
Now the input is pressed until the next frame;
Here, I don't acually know if I'm going too deep into the subject, or on the right way.
I'm actually confused with the relationship of the game logic time with the keyboard buffer time, since they have different time resolutions, are called in diferent times etc. The game logic time has microsecond resolution, but the keyboard has real time resolution, so the only timer that its has a relationship to be synchronized its the game render time (the real game time).
From the fixed-time step simulation point-of-view, makes sense to consume FIXED_TIME_STEP of input each logical tick, which means that each logical tick I can consume any inputs that ocurred until the game logic time (?). Eg. :
m_tRenderTime.Update();
UINT64 ui64CurMicros = m_tRenderTime.CurMicros();
while (ui64CurMicros - m_tLogicTime.CurTime() > FIXED_TIME_STEP) {
kbKeyboardBuffer.ConsumeKeyEvents(&kKeyboard, m_tLogicTime.CurTime());
NextState();
if ( !m_smStateMachine.Tick(this) ) { return false; }
m_tLogicTime.UpdateBy( FIXED_TIME_STEP );
}
...but since the time that the inputs was ocurred is different from the time that the keyboard time is updated (in the Consume... method), they won't be synchronized with the game time.
I've synchronized the input time with all my game timers by just copying the game timers into the input timer at the start of the game, so this shouldn't be a problem.
Can you guys give me an advice on these specific questions?