// [meta] an auxilliary function to the main update loop: static void DoUpdates(const UpdateState& u) // Runs the update functions using the given update state. { // Run UI update. UI::Update(u); // Run object updates (physics & behavior). Model::Update(u); // Run game logic (course timers, etc.) Game::Update(u); // Deal with network? } // [meta] From the main update loop: // Update the game logic & physics. int UpdateCount = 20; // For protection against update functions taking longer than the sampling period. UpdateState u; while (UpdatesPending() && UpdateCount-- > 0) { GetNextUpdateState(&u); DoUpdates(u); } GetNextUpdateState(&u); DoUpdates(u); // [meta] then does rendering... // [meta] Here's the code for the input polling thread: #include // For input thread stuff. #include #include bool StopInputsThread = false; unsigned long ServiceInputsThread = -1; void _cdecl ServiceInputsThreadFunction(void*); // [meta] during initialization: // Start input thread. StopInputsThread = false; ServiceInputsThread = _beginthread(ServiceInputsThreadFunction, 0, 0); if (ServiceInputsThread == -1) { Error e; e << "Can't create thread to poll input."; throw e; } // // Input polling thread. // const int MIN_SAMPLE_PERIOD = 33; // ~33 Hz. Sample at least this often (in ms). HANDLE PauseInputThread = NULL; HANDLE BufferMutex = NULL; int NextSampleTime = 0; int LastSampleTime = 0; const int SAMPLE_BUFFER_BITS = 4; // 16-sample buffer. const int MAX_SAMPLES = 1 << SAMPLE_BUFFER_BITS; const int BUFFER_MASK = MAX_SAMPLES - 1; UpdateState Buffer[MAX_SAMPLES]; int Front = 0; int Rear = 0; int BufferCount() { return (Front - Rear) & BUFFER_MASK; } bool SampleInputsAndAddToBuffer() // Locks the input buffer, samples the control inputs, and inserts a new UpdateState // into the buffer. // Returns false if the buffer is full or it can't acquire a lock on the input buffer. { if (BufferCount() >= 15 || WaitForSingleObject(BufferMutex, 0) == WAIT_FAILED) { return false; } UpdateState* u = &Buffer[Front]; // Fill timer values. u->Ticks = Timer::GetTicks(); u->DeltaTicks = u->Ticks - LastSampleTime; if (u->DeltaTicks <= 0) u->DeltaTicks = 1; // Don't allow a 0 time delta. if (u->DeltaTicks > 100) u->DeltaTicks = 100; // Limit max time delta. u->DeltaT = u->DeltaTicks / 1000.0; // Poll user input. Input::GetInputState(&u->Inputs); // Remember when we sampled. LastSampleTime = u->Ticks; // Schedule next sample. NextSampleTime = u->Ticks + MIN_SAMPLE_PERIOD; // Commit the sample. Front = (Front + 1) & BUFFER_MASK; // Unlock the buffer. ReleaseMutex(BufferMutex); return true; } bool UpdatesPending() // Returns true if there are update samples in the queue waiting to be processed. { // Check buffer. return BufferCount() > 0; } void GetNextUpdateState(UpdateState* u) // Returns the next update to be processed. If the update buffer is empty, then // polls the control inputs and returns their current values. { // Timeout on this, to be safe? while (BufferCount() == 0) { SampleInputsAndAddToBuffer(); } *u = Buffer[Rear]; // Copy sampled data. Rear = (Rear + 1) & BUFFER_MASK; } void _cdecl ServiceInputsThreadFunction(void* dummy) // Thread procedure which periodically polls the inputs and the time and puts // the results in a buffer for retrieval by the main game loop. { BufferMutex = CreateMutex(NULL, FALSE, "input-buffer-mutex"); while (!StopInputsThread) { // wait for PauseInputThread; // Sleep while waiting until it's time to collect input. int Ticks = Timer::GetTicks(); int SleepTime = NextSampleTime - Ticks; if (SleepTime > 0) { Sleep(SleepTime); } // If it's time to collect input, then go for it. Ticks = Timer::GetTicks(); if (Ticks >= NextSampleTime) { SampleInputsAndAddToBuffer(); // get inputs, & timestamp, & add to buffer; } } }