PageRenderTime 627ms CodeModel.GetById 433ms app.highlight 11ms RepoModel.GetById 114ms app.codeStats 0ms

/Engine/Game/GameTime.cs

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