Jump to content

  • Log In with Google      Sign In   
  • Create Account

We need your help!

We need 1 more developer from Canada and 12 more from Australia to help us complete a research survey.

Support our site by taking a quick sponsored survey and win a chance at a $50 Amazon gift card. Click here to get started!


Basic Game Loop


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
12 replies to this topic

#1 Xanather   Members   -  Reputation: 753

Like
0Likes
Like

Posted 27 July 2012 - 08:57 AM

Hello there, I quickly developed this solution to developing a fixed time step for a server. (client is XNA so time step is already taken care of there).


[source lang="csharp"] Stopwatch stopwatch = new Stopwatch(); float timer = (1f / 60f) * 1000000; while (true) { stopwatch.Reset(); stopwatch.Start(); //start update world //end update world Thread.Sleep(1); while (stopwatch.ElapsedTicks < timer) { } Console.WriteLine(stopwatch.ElapsedTicks); stopwatch.Stop(); }[/source]

Its pretty basic, it sleeps for 1 millisecond to prevent 100% CPU usage (well really it sleeps for less than 15-16 milliseconds just because of the way Thread.Sleep works) then uses the stopwatch to see when its time to loop again. It does print out 16667 every cycle which is good, except for some times when it abnormaly printed out 32000+ (32 milliseconds). I am guessing this is due to the point of how Thread.Sleep works. The question is: is the code above good for a completely fixed time-step? should I also make use of winmm.dll (i think thats what it was called) for a more accurate fixed time step?

Also id love to not use Thread.Sleep() but I just cant find another way, the server is console based.

Replies are appriciated, Thanks!

Edited by Xanather, 27 July 2012 - 09:04 AM.


Sponsor:

#2 Neometron   Members   -  Reputation: 485

Like
0Likes
Like

Posted 27 July 2012 - 09:44 AM

I'm assuming this is c#
This might help http://msdn.microsof...y/ff650674.aspx

EDIT:
I realized after some reading that the Stopwatch uses the high performance counter.

I haven't really programmed in c# but here is my attempt:

[source lang="csharp"]using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Diagnostics;using System.Threading;namespace StopwatchApp{class TimeStep{ public TimeStep(float Step) { Enable = true; mStep = Step; mCurrent = Stopwatch.GetTimestamp(); mLast = mCurrent; mFrequency = Stopwatch.Frequency; } public void Reset() {mLast = Stopwatch.GetTimestamp();} public bool WaitNextStep() { if(Enable) { mCurrent = Stopwatch.GetTimestamp(); long diff = mCurrent - mLast; mDelta = (float)diff / (float)mFrequency; float msDelta = 1000.0f * mDelta; float msStep = 1000.0f * mStep; if(msDelta < msStep) { int msSleep = (int)(msStep - msDelta); if (msSleep == 0) msSleep = 1; Thread.Sleep(msSleep); } mLast = mCurrent; } else return false; return true; } public float GetDelta() {return mDelta;} public bool Enable; private long mFrequency; private long mLast; private long mCurrent; private float mStep; private float mDelta;}class Program{ static void Main(string[] args) { TimeStep LoopStep = new TimeStep(1.0f/60.0f); while (LoopStep.WaitNextStep()) { float fDelta = LoopStep.GetDelta(); // Do stuff Console.Clear(); Console.Write(fDelta); if (Console.KeyAvailable) { LoopStep.Enable = false; } } }}}[/source]

Never mind, this has the exact problem you are having. It does boil down to Thread.Sleep(msSleep). I think it's the nature of the OS returning control to your app.

Edited by Neometron, 27 July 2012 - 12:14 PM.


#3 Neometron   Members   -  Reputation: 485

Like
0Likes
Like

Posted 27 July 2012 - 01:43 PM

After playing with Thread.Sleep I learned this:

Thread.Sleep(1) = ~15ms
Thread.Sleep(15) = ~15ms
Thread.Sleep(16) = ~30ms

Windows probably allocates 15ms per thread and then switches to next thread.

#4 Xanather   Members   -  Reputation: 753

Like
0Likes
Like

Posted 27 July 2012 - 09:20 PM

Grr, this is anoying. I thought creating a basic time-specified loop would be extremely easy, but instead it turns out windows does not like that... I will probably use this solution for the time being, the only problem is 10% of the CPU is being used all the time when using this method (and I have 8 cores), so really more than 50% of the core is being used. Thanks for the replies.

Edited by Xanather, 27 July 2012 - 09:22 PM.


#5 krippy2k8   Members   -  Reputation: 646

Like
4Likes
Like

Posted 27 July 2012 - 09:43 PM

Sleep works via the Windows system timer (which uses the Programmable Interrupt Controller), which has a default resolution of 15.6ms on consumer versions of Windows. You can change the system timer resolution with the multimedia functions timeBeginPeriod and timeEndPeriod. But bear in mind that the system timer is a global system timer, so it will increase the timer resolution for every application running on the system and will cause the system to use more energy, though it should not eat all that much CPU.

Here is a white paper from Microsoft regarding system timer resolution:

Timers, Timer Resolution, and Development of Efficient Code

That being said, using Sleep to facilitate a fixed-step game loop may not give you the best results.

Instead you can use something like timeSetEvent or CreateTimerQueueTimer to set a periodic timer. While you will not be able to guarantee that your loop executes exactly every n milliseconds (nothing will), it will guarantee that it will be called exactly once for every n milliseconds elapsed. The difference is that, for instance, if your timer is set to fire every 16ms, and some other high priority thread hogs the CPU for 64ms, when it is free then your thread will immediately receive 4 callbacks for the 4 16ms periods that have elapsed, which will allow your simulation to catch up.

Sorry, not sure right off the bat how to use these from C#, but if you Google it you can probably find out.

Edited by krippy2k8, 27 July 2012 - 10:08 PM.


#6 Xanather   Members   -  Reputation: 753

Like
0Likes
Like

Posted 27 July 2012 - 11:27 PM

i will look into that Windows API, thank you :)

#7 darookie   Members   -  Reputation: 1437

Like
1Likes
Like

Posted 28 July 2012 - 07:05 AM

Another method that doesn't require fiddling with the Windows API would be to use an event instead:

[source lang="csharp"]using System;using System.Diagnostics;using System.Threading;static class P{ static void Main() { var stopwatch = new Stopwatch(); var timer = (int)Math.Round((1f / 60f) * 1000f); var wait = new ManualResetEvent(false); while (true) { stopwatch.Reset(); stopwatch.Start(); //start update world //end update world wait.WaitOne((int)Math.Max(0, timer - stopwatch.ElapsedMilliseconds)); Console.WriteLine(stopwatch.ElapsedMilliseconds); stopwatch.Stop(); } }}[/source]

This approach will work with a millisecond resolution only, but I seriously doubt that a higher resolution would be required anyway... especially given the fact that Windows is not a real-time OS and won't allocate thread execution time slots at a significantly higher resolution anyway :)

Edited by darookie, 28 July 2012 - 07:09 AM.


#8 Firestryke31   Members   -  Reputation: 350

Like
1Likes
Like

Posted 28 July 2012 - 04:48 PM

What about something like this (pseudocode, no idea how to use XNA):

// Init timekeeping variables

step = 0;
timeSinceLastUpdate = 0;

while(notDeadYet)
{
	// total up the time pool, including leftovers from the last run through
	step += timeSinceLastUpdate;
	// Get the start of the update
	start = currentTime();

	// as long as there's enough time to step, step
	while(step > timestepAmount)
	{
		stepGameState();

		// Take a step out of the time pool
		step -= timestepAmount;
	}
	render(); // Or not, it's whatever

	// How long did that update take? If you're not rendering, you best hope it's less than timestepAmount
	// or that variations are due to external processes or else your system is in the positive feedback loop
	// of death and despair and youneedoptimisationness
	timeSinceLastUpdate = currentTime() - start;
}

Basically, instead of hoping your system runs once a frame, you see how long it was since it last ran, and run it as long as there are ticks to run; i.e. if your step is 15ms and the game loop takes 30ms due to the scheduler or renderer, the game ticks twice, and if the game loop takes 14ms, it doesn't tick at all because it's not ready yet. I used += for step to accumulate partial ticks so the loop won't have a problem with two 14ms updates in a row, and if you pass the partial amount to the renderer it can use that to smooth animations and other motion. You could also probably modify it to step one tick into the "future" and use the partial tick to interpolate between the states to display instead of extrapolate to a guess (though at 60FPS I don't know how visible this is).

#9 krippy2k8   Members   -  Reputation: 646

Like
0Likes
Like

Posted 28 July 2012 - 07:05 PM

Another method that doesn't require fiddling with the Windows API would be to use an event instead:


Events with timeouts depend on the system clock too, so this will behave exactly the same as using Sleep.

#10 krippy2k8   Members   -  Reputation: 646

Like
0Likes
Like

Posted 28 July 2012 - 07:10 PM

What about something like this (pseudocode, no idea how to use XNA):


That would typically be my preferred method since you don't need to rely on or change the granularity of the system clock, but it will result in 100% CPU usage, which is precisely what he was trying to avoid by using Sleep. I'm not sure why that is a big deal in a game server unless he plans on running many instances of the game server application on the same machine at the same time.

#11 Firestryke31   Members   -  Reputation: 350

Like
1Likes
Like

Posted 28 July 2012 - 08:21 PM

Throw in a Sleep() before/after render? Or if it's a server then instead of rendering.

Edited by Firestryke31, 28 July 2012 - 08:22 PM.


#12 Xanather   Members   -  Reputation: 753

Like
0Likes
Like

Posted 29 July 2012 - 07:06 AM

Darookie, never knew of that ManualResetEvent method (I need to dig into .net more lol), thanks I will try your method.

Firestryke, that seems like a alright idea (only because this is the server im talking about), even with using Thread.Sleep(x) I can make sure the world is updated 60 times a second with that code, once I finish developing the server I may attempt to use that idea and see how well it works aswell. Thanks :)

#13 Inferiarum   Members   -  Reputation: 735

Like
0Likes
Like

Posted 29 July 2012 - 02:31 PM

Firestrykes approach is also described in the well known article
http://gafferongames.com/game-physics/fix-your-timestep/




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS