private void TickInternal() { try { // If this instance is existing, then don't make any further update/draw if (isExiting) { CheckEndRun(); return; } // If this instance is not active, sleep for an inactive sleep time if (!IsActive) { Utilities.Sleep(InactiveSleepTime); return; } // Update the timer timer.Tick(); // Update the playTimer timer playTimer.Tick(); // Measure updateTimer updateTimer.Reset(); var elapsedAdjustedTime = timer.ElapsedTimeWithPause; if (forceElapsedTimeToZero) { elapsedAdjustedTime = TimeSpan.Zero; forceElapsedTimeToZero = false; } if (elapsedAdjustedTime > maximumElapsedTime) { elapsedAdjustedTime = maximumElapsedTime; } bool suppressNextDraw = true; int updateCount = 1; var singleFrameElapsedTime = elapsedAdjustedTime; var drawLag = 0L; if (IsFixedTimeStep) { // If the rounded TargetElapsedTime is equivalent to current ElapsedAdjustedTime // then make ElapsedAdjustedTime = TargetElapsedTime. We take the same internal rules as XNA if (Math.Abs(elapsedAdjustedTime.Ticks - TargetElapsedTime.Ticks) < (TargetElapsedTime.Ticks >> 6)) { elapsedAdjustedTime = TargetElapsedTime; } // Update the accumulated time accumulatedElapsedGameTime += elapsedAdjustedTime; // Calculate the number of update to issue if (ForceOneUpdatePerDraw) { updateCount = 1; } else { updateCount = (int)(accumulatedElapsedGameTime.Ticks / TargetElapsedTime.Ticks); } if (IsDrawDesynchronized) { drawLag = accumulatedElapsedGameTime.Ticks % TargetElapsedTime.Ticks; suppressNextDraw = false; } else if (updateCount == 0) { // If there is no need for update, then exit return; } // Calculate a moving average on updateCount lastUpdateCount[nextLastUpdateCountIndex] = updateCount; float updateCountMean = 0; for (int i = 0; i < lastUpdateCount.Length; i++) { updateCountMean += lastUpdateCount[i]; } updateCountMean /= lastUpdateCount.Length; nextLastUpdateCountIndex = (nextLastUpdateCountIndex + 1) % lastUpdateCount.Length; // Test when we are running slowly drawRunningSlowly = updateCountMean > updateCountAverageSlowLimit; // We are going to call Update updateCount times, so we can substract this from accumulated elapsed game time accumulatedElapsedGameTime = new TimeSpan(accumulatedElapsedGameTime.Ticks - (updateCount * TargetElapsedTime.Ticks)); singleFrameElapsedTime = TargetElapsedTime; } else { Array.Clear(lastUpdateCount, 0, lastUpdateCount.Length); nextLastUpdateCountIndex = 0; drawRunningSlowly = false; } bool beginDrawSuccessful = false; try { beginDrawSuccessful = BeginDraw(); // Reset the time of the next frame for (lastFrameElapsedGameTime = TimeSpan.Zero; updateCount > 0 && !isExiting; updateCount--) { updateTime.Update(totalUpdateTime, singleFrameElapsedTime, singleFrameUpdateTime, drawRunningSlowly, true); try { UpdateAndProfile(updateTime); if (EarlyExit) { return; } // If there is no exception, then we can draw the frame suppressNextDraw &= suppressDraw; suppressDraw = false; } finally { lastFrameElapsedGameTime += singleFrameElapsedTime; totalUpdateTime += singleFrameElapsedTime; } } // End measuring update time updateTimer.Tick(); singleFrameUpdateTime += updateTimer.ElapsedTime; // Update game time just before calling draw //updateTime.Update(totalUpdateTime, singleFrameElapsedTime, singleFrameUpdateTime, drawRunningSlowly, true); if (!suppressNextDraw) { totalDrawTime = TimeSpan.FromTicks(totalUpdateTime.Ticks + drawLag); DrawInterpolationFactor = drawLag / (float)TargetElapsedTime.Ticks; DrawFrame(); } singleFrameUpdateTime = TimeSpan.Zero; } finally { if (beginDrawSuccessful) { using (Profiler.Begin(GameProfilingKeys.GameEndDraw)) { EndDraw(true); if (gamePlatform.MainWindow.IsMinimized || gamePlatform.MainWindow.Visible == false || (gamePlatform.MainWindow.Focused == false && TreatNotFocusedLikeMinimized)) { MinimizedMinimumUpdateRate.Throttle(out _); } else { WindowMinimumUpdateRate.Throttle(out _); } } } CheckEndRun(); } } catch (Exception ex) { Log.Error("Unexpected exception", ex); throw; } }
/// <summary> /// Calls <see cref="RawTick"/> automatically based on this game's setup, override it to implement your own system. /// </summary> protected virtual void RawTickProducer() { try { // Update the timer autoTickTimer.Tick(); var elapsedAdjustedTime = autoTickTimer.ElapsedTimeWithPause; if (forceElapsedTimeToZero) { elapsedAdjustedTime = TimeSpan.Zero; forceElapsedTimeToZero = false; } if (elapsedAdjustedTime > maximumElapsedTime) { elapsedAdjustedTime = maximumElapsedTime; } bool drawFrame = true; int updateCount = 1; var singleFrameElapsedTime = elapsedAdjustedTime; var drawLag = 0L; if (suppressDraw || Window.IsMinimized && DrawWhileMinimized == false) { drawFrame = false; suppressDraw = false; } if (IsFixedTimeStep) { // If the rounded TargetElapsedTime is equivalent to current ElapsedAdjustedTime // then make ElapsedAdjustedTime = TargetElapsedTime. We take the same internal rules as XNA if (Math.Abs(elapsedAdjustedTime.Ticks - TargetElapsedTime.Ticks) < (TargetElapsedTime.Ticks >> 6)) { elapsedAdjustedTime = TargetElapsedTime; } // Update the accumulated time accumulatedElapsedGameTime += elapsedAdjustedTime; // Calculate the number of update to issue if (ForceOneUpdatePerDraw) { updateCount = 1; } else { updateCount = (int)(accumulatedElapsedGameTime.Ticks / TargetElapsedTime.Ticks); } if (IsDrawDesynchronized) { drawLag = accumulatedElapsedGameTime.Ticks % TargetElapsedTime.Ticks; } else if (updateCount == 0) { drawFrame = false; // If there is no need for update, then exit return; } // We are going to call Update updateCount times, so we can substract this from accumulated elapsed game time accumulatedElapsedGameTime = new TimeSpan(accumulatedElapsedGameTime.Ticks - (updateCount * TargetElapsedTime.Ticks)); singleFrameElapsedTime = TargetElapsedTime; } RawTick(singleFrameElapsedTime, updateCount, drawLag / (float)TargetElapsedTime.Ticks, drawFrame); var window = gamePlatform.MainWindow; if (gamePlatform.IsBlockingRun) // throttle fps if Game.Tick() called from internal main loop { if (window.IsMinimized || window.Visible == false || (window.Focused == false && TreatNotFocusedLikeMinimized)) { MinimizedMinimumUpdateRate.Throttle(out _); } else { WindowMinimumUpdateRate.Throttle(out _); } } } catch (Exception ex) { Log.Error("Unexpected exception", ex); throw; } }