PageRenderTime 43ms CodeModel.GetById 11ms RepoModel.GetById 1ms app.codeStats 0ms

/Engine/Game/GameTime.cs

#
C# | 368 lines | 219 code | 37 blank | 112 comment | 15 complexity | d6c5cbf21f36be5d03a7fc7663cf3e8e MD5 | raw file
Possible License(s): Apache-2.0
  1. using Delta.Engine.Dynamic;
  2. using Delta.Utilities;
  3. namespace Delta.Engine.Game
  4. {
  5. /// <summary>
  6. /// Game Time is used for having a pausable and resetable time source,
  7. /// which is useful for games that can pause.<para />
  8. /// Dependant systems are Effects, Physics.
  9. /// </summary>
  10. public class GameTime : DynamicModule
  11. {
  12. #region CheckEvery (Static)
  13. /// <summary>
  14. /// Check every time (e.g. one second, half a second, every 10 seconds)
  15. /// if the time has passed and return true once in that time frame and
  16. /// false otherwise. Used for spawning stuff, e.g. every 0.5 seconds and
  17. /// is very useful to write framerate independent code.
  18. /// Note: If using a small value here like 1.0f/100.0f and we have less
  19. /// fps (e.g. 60), then this will return true every time, but obviously
  20. /// we cannot return true 100 times if this method is only called 60 times.
  21. /// </summary>
  22. /// <param name="timeStep">Time step in seconds</param>
  23. /// <returns>
  24. /// True if the timeStep is reached, false if we have too many frames and
  25. /// need to wait until timeStep is reached next.
  26. /// </returns>
  27. public static bool CheckEvery(float timeStep)
  28. {
  29. // Make sure the timeStep value is valid!
  30. if (timeStep <= 0)
  31. {
  32. Log.Warning("Invalid time step=" + timeStep);
  33. return true;
  34. }
  35. // Calculate with millisecond values calculated in the timer.
  36. return (int)(Milliseconds / (1000 * timeStep)) >
  37. (int)(LastMilliseconds / (1000 * timeStep));
  38. }
  39. /// <summary>
  40. /// Check every time (e.g. one second, half a second, every 10 seconds)
  41. /// if the time has passed and return true once in that time frame and
  42. /// false otherwise. Used for spawning stuff, e.g. every 0.5 seconds and
  43. /// is very useful to write framerate independent code.
  44. /// Note: If using a small value here like 1.0f/100.0f and we have less
  45. /// fps (e.g. 60), then this will return true every time, but obviously
  46. /// we cannot return true 100 times if this method is only called 60 times.
  47. /// </summary>
  48. /// <param name="timeStep">Time step in seconds</param>
  49. /// <param name="startTimeMs">
  50. /// Only start checking when this time is reached and then every time step
  51. /// after (in milliseconds).
  52. /// </param>
  53. /// <returns>
  54. /// True if the timeStep is reached, false if we have too many frames and
  55. /// need to wait until timeStep is reached next.
  56. /// </returns>
  57. public static bool CheckEvery(float timeStep, long startTimeMs)
  58. {
  59. // Make sure the timeStep value is valid!
  60. if (startTimeMs < 0 ||
  61. timeStep <= 0)
  62. {
  63. Log.Warning("Invalid timeStep='" + timeStep +
  64. "' or startTimeMs='" + startTimeMs + "'");
  65. return true;
  66. }
  67. // Calculate with millisecond values calculated in the timer.
  68. long relativeStartMs = Milliseconds - startTimeMs;
  69. if (relativeStartMs < 0)
  70. {
  71. // Not started yet
  72. return false;
  73. } // if
  74. long relativeLastMs = LastMilliseconds - startTimeMs;
  75. return (int)(relativeStartMs / (1000 * timeStep)) >
  76. (int)(relativeLastMs / (1000 * timeStep));
  77. }
  78. #endregion
  79. #region GetDeltaToNow (Static)
  80. /// <summary>
  81. /// Get delta to now in seconds as a floating point value.
  82. /// </summary>
  83. /// <param name="startTimeInMs">The start time in ms.</param>
  84. /// <returns>Float Delta to Now</returns>
  85. public static float GetDeltaToNow(long startTimeInMs)
  86. {
  87. return (Milliseconds - startTimeInMs) / 1000.0f;
  88. }
  89. #endregion
  90. #region IsRunning (Static)
  91. /// <summary>
  92. /// Flag if the game time is updating or not.
  93. /// </summary>
  94. public static bool IsRunning
  95. {
  96. get
  97. {
  98. return Instance.isRunning;
  99. }
  100. }
  101. #endregion
  102. #region Milliseconds (Static)
  103. /// <summary>
  104. /// The elapsed game time in milliseconds.
  105. /// </summary>
  106. public static long Milliseconds
  107. {
  108. get
  109. {
  110. return Instance.milliseconds;
  111. }
  112. }
  113. #endregion
  114. #region LastMilliseconds (Static)
  115. /// <summary>
  116. /// The elapsed game time in milliseconds from the last frame. This allows
  117. /// us to do some checks from frame to frame to see if a timeout or
  118. /// cooldown has been reached. See CheckEvery for a use case.
  119. /// </summary>
  120. public static long LastMilliseconds
  121. {
  122. get
  123. {
  124. return Instance.lastMilliseconds;
  125. }
  126. }
  127. #endregion
  128. #region Delta (Static)
  129. /// <summary>
  130. /// Returns the current delta time. It represents the difference between
  131. /// the last tick and the current tick in seconds (usually small values).
  132. /// Used for many calculations and updates to make sure the time goes
  133. /// on constantly no matter how many frames we have per second.
  134. /// </summary>
  135. public static float Delta
  136. {
  137. get
  138. {
  139. return Instance.delta;
  140. }
  141. }
  142. #endregion
  143. #region TotalTime (Static)
  144. /// <summary>
  145. /// The total time since the game time was started.
  146. /// </summary>
  147. public static float TotalTime
  148. {
  149. get
  150. {
  151. return Instance.totalTime;
  152. }
  153. }
  154. #endregion
  155. #region Paused (event)
  156. /// <summary>
  157. /// Event which will occur everytime the game time is paused.
  158. /// </summary>
  159. public static event RunDelegate Paused;
  160. #endregion
  161. #region Resumed (event)
  162. /// <summary>
  163. /// Event which will occur everytime the game time is resumed again.
  164. /// </summary>
  165. public static event RunDelegate Resumed;
  166. #endregion
  167. #region Private
  168. #region cachedInstance (Private)
  169. private static GameTime cachedInstance;
  170. #endregion
  171. #region Instance (Private)
  172. private static GameTime Instance
  173. {
  174. get
  175. {
  176. if (cachedInstance == null)
  177. {
  178. cachedInstance = Factory.Create<GameTime>();
  179. }
  180. return cachedInstance;
  181. }
  182. }
  183. #endregion
  184. #region timeMsOffset (Private)
  185. private long timeMsOffset;
  186. #endregion
  187. #region pauseStartOffset (Private)
  188. private long pauseStartOffset;
  189. #endregion
  190. #region isRunning (Private)
  191. private bool isRunning;
  192. #endregion
  193. #region totalTime (Private)
  194. /// <summary>
  195. /// The total time since the game time was started.
  196. /// </summary>
  197. private float totalTime;
  198. #endregion
  199. #region delta (Private)
  200. /// <summary>
  201. /// Returns the current delta time.
  202. /// </summary>
  203. private float delta;
  204. #endregion
  205. #region milliseconds (Private)
  206. /// <summary>
  207. /// Milliseconds the game time has been running.
  208. /// </summary>
  209. private long milliseconds;
  210. #endregion
  211. #region lastMilliseconds
  212. /// <summary>
  213. /// Milliseconds from last frame, allows us to do frame by frame checks.
  214. /// </summary>
  215. private long lastMilliseconds;
  216. #endregion
  217. #region wasRunningBeforeAppPause
  218. /// <summary>
  219. /// Flag if the game time was running before it was paused by the
  220. /// system information event. If we don't save this state we might
  221. /// start the game time again accidentally in OnAppResume.
  222. /// </summary>
  223. private bool wasRunningBeforeAppPause;
  224. #endregion
  225. #endregion
  226. #region Constructors
  227. /// <summary>
  228. /// Create a new game time instance, which is started by default, but can
  229. /// be stopped or restarted by the game if a new level starts.
  230. /// </summary>
  231. internal GameTime()
  232. : base("GameTime", typeof(Time))
  233. {
  234. wasRunningBeforeAppPause = false;
  235. isRunning = true;
  236. timeMsOffset += Time.Milliseconds - pauseStartOffset;
  237. Application.Information.OnAppPause += OnAppPause;
  238. Application.Information.OnAppResume += OnAppResume;
  239. }
  240. /// <summary>
  241. /// Remove OnAppPause and OnAppResume event handlers added in constructor.
  242. /// </summary>
  243. ~GameTime()
  244. {
  245. Application.Information.OnAppPause -= OnAppPause;
  246. Application.Information.OnAppResume -= OnAppResume;
  247. }
  248. #endregion
  249. #region OnAppPause
  250. private void OnAppPause()
  251. {
  252. wasRunningBeforeAppPause = isRunning;
  253. Pause();
  254. }
  255. #endregion
  256. #region OnAppResume
  257. private void OnAppResume()
  258. {
  259. if (wasRunningBeforeAppPause)
  260. {
  261. Start();
  262. wasRunningBeforeAppPause = false;
  263. }
  264. }
  265. #endregion
  266. #region Start (Static)
  267. /// <summary>
  268. /// Start or resume the game time updating.
  269. /// </summary>
  270. public static void Start()
  271. {
  272. if (IsRunning == false)
  273. {
  274. Instance.isRunning = true;
  275. Instance.timeMsOffset += Time.Milliseconds - Instance.pauseStartOffset;
  276. // Still notfiy the listeners (if required)
  277. if (Resumed != null &&
  278. // but only if the time si resumed from a previous pause
  279. TotalTime > 0.0f)
  280. {
  281. Resumed();
  282. } // if
  283. }
  284. }
  285. #endregion
  286. #region Pause (Static)
  287. /// <summary>
  288. /// Pause the game time updating.
  289. /// </summary>
  290. public static void Pause()
  291. {
  292. if (IsRunning)
  293. {
  294. Instance.isRunning = false;
  295. Instance.pauseStartOffset = Time.Milliseconds;
  296. // Still notfiy the listeners (if required)
  297. if (Paused != null)
  298. {
  299. Paused();
  300. } // if
  301. }
  302. }
  303. #endregion
  304. #region Reset (Static)
  305. /// <summary>
  306. /// Reset the game time.
  307. /// </summary>
  308. public static void Reset()
  309. {
  310. Instance.totalTime = 0.0f;
  311. }
  312. #endregion
  313. #region Run (Public)
  314. /// <summary>
  315. /// Update the game time.
  316. /// </summary>
  317. public override void Run()
  318. {
  319. if (IsRunning == false)
  320. {
  321. delta = 0.0f;
  322. return;
  323. }
  324. lastMilliseconds = milliseconds;
  325. milliseconds = Time.Milliseconds - timeMsOffset;
  326. delta = Time.Delta;
  327. totalTime += Delta;
  328. }
  329. #endregion
  330. }
  331. }