#pragma region CPL License
/*
Nuclex Native Framework
Copyright (C) 2002-2013 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#pragma endregion // CPL License
#ifndef NUCLEX_GAME_TIMING_SCALEDTIMER_H
#define NUCLEX_GAME_TIMING_SCALEDTIMER_H
#include "Timer.h"
namespace Nuclex { namespace Game { namespace Timing {
// ------------------------------------------------------------------------------------------- //
/// Timer designed to facilitate time-scaled updating
///
///
/// Frame rate independent movement can be implemented in two ways: either via
/// time scaling or via time stepping. Time scaling will scale the movements of all
/// objects in a game by the amount of time passed since the last frame. Time stepping
/// advances time in fixed steps, multiple times if more time has passed than the length
/// of a single step.
///
///
/// This timer is intended for the scaled approach. Its advantage is a little less CPU
/// load on low end systems (where a stepped timer would be doing several steps each
/// cycle). However, physics simulations tend to get unstable if time jumps by larger
/// amounts and scaling object movements by time means that multiplayer states will
/// eventually drift apart due to different rounding errors occuring as a result of speed
/// differences between the connected systems.
///
///
///
/// scaledTimer.Reset();
///
/// while(!this->quitRequested) {
/// GameTime deltaTime = scaledTimer.GetElapsedAndReset();
///
/// UpdateAll(deltaTime);
/// DrawAll(deltaTime);
///
/// RunMessagePump();
/// }
///
///
///
/// The time returned from GetElapsed() or GetElapsedAndReset() is the number of
/// completely elapsed microseconds. The fractional part of the currently running
/// microsecond is kept until the next call to GetElapsed() or GetElapsedAndReset(),
/// guaranteeing that no time is ever lost. The same goes for when the provided Clock's
/// frequency is not evenly divisible to microseconds.
///
///
class ScaledTimer : public Timer {
/// Initializes a new scaled timer using the default clock
public: NUCLEX_GAME_API ScaledTimer();
/// Initializes a new scaled timer using the specified clock
/// Clock the scaled timer will use
public: NUCLEX_GAME_API ScaledTimer(const std::shared_ptr &clock);
/// Destroys the scaled timer
public: NUCLEX_GAME_API virtual ~ScaledTimer() {}
/// Pauses the simulation clock
///
/// Real world time will still continue running, but the timer's simulation time
/// will stop counting until the ResumeSimulation() or Reset() methods are called.
///
public: NUCLEX_GAME_API void PauseSimulation();
/// Resumes the simulation clock after it has been paused
public: NUCLEX_GAME_API void ResumeSimulation();
/// Resets the delta times to zero
///
///
/// You should call this method once immediately before entering the main loop so the
/// time accumulated between the time provider being created and your game becoming
/// ready to run will not result in a jump of possibly several seconds of game time
/// being skipped/caught up.
///
///
/// Do not use this method in your normal update loop as this will result in the time
/// that passes between retrieving the timer's delta time or steps and calling Reset(),
/// leading to minuscule speed differences depending on the operating system's CPU load
/// and performance.
///
///
/// If the simulation clock was paused, calling Reset() will also restore it running.
///
///
public: NUCLEX_GAME_API void Reset();
/// Returns the elapsed time since the timer was last reset
/// The time that has elapsed since the last Reset() call
///
/// This method is identical to GetElapsedAndReset() except that it doesn't reset
/// the delta times, allowing you to "peek" at what the timing values would
/// be right now. Do not use this method in your game loop to advance time due to
/// the effects described in the documentation of the Reset() method.
///
public: NUCLEX_GAME_API GameTime GetElapsed() const;
///
/// Returns the amount of time elapsed since the last call and resets the elapsed time
///
/// The time that has elapsed since the last Reset() call
///
///
/// Use this method in a time-scaled game loop to determine the amount of time that
/// has elapsed since the previous frame. Time will be advanced in such a way that
/// all of clocks ticks will be accounted for. If the elapsed time is not evenly
/// divisible to the next millisecond, the remaining clock ticks will be budgeted
/// for the next frame.
///
///
public: NUCLEX_GAME_API GameTime GetElapsedAndResetDelta();
/// Accumulated simulation time during the intervals where it was running
private: std::chrono::microseconds accumulatedSimulationUs;
/// Time since which the simulation clock has been running
private: std::chrono::microseconds simulationResumeUs;
/// Total elapsed simulation time in microseconds
private: std::chrono::microseconds totalSimulationUs;
/// Total elapsed time in the real world in microseconds
private: std::chrono::microseconds totalRealWorldUs;
};
// ------------------------------------------------------------------------------------------- //
}}} // namespace Nuclex::Game::Timing
#endif // NUCLEX_GAME_TIMING_WINDOWSTIMEPROVIDER_H