My game is out of control!

closed account (3pj6b7Xj)
I need help on this one. I've been writing code to try to get my game to run at the same speed in all different types of systems. As I understand it, I need to control the amount of frames per second the game is pumping out through the windows GDI.

It runs fine on my system but on my friends computer, the snake is two times faster, I've tried and tried and can't really test my theories since I only can test on my system the most.

I thought I had it for a second but didn't. Every system is faster than another and it affects the speed of the snakes on the screen.

If you guys can help out, that is appreciated! The snakes have an internal timer for controling their speed but my problem is, I need to have some kind of check at the start of the game that checks the speed of the system and supplies the snakes with the appropriate number to use for the timer so that the snakes run at the same speed across all systems.

If I can get this little issue corrected, I can release this game to the public along with the source code. I have already written documentation for it and thinking about making it go live on Download.com or maybe some C++ site dedicated to windows games or something, i'm really not sure but I need to correct this so I can continue my other projects!

Thanks again all!
Well before we say anything else, how are you currently getting the time? A system independant timer is not an easy thing to achieve, although it sounds like it should be.
I believe the following may be of use: http://sfml-dev.org/documentation/1.6/Platform_8cpp-source.htm

Are you making your game in a console, or using a graphics library?
Last edited on
err, is it the same operating system? if so then they probably have a different processer.
I'm sure you could solve the problem on your own, but i would probably make a different version of the game for different computers. If you both have windows computers, then i just might be able to know what is going on. I don't know anything about linux, soloris, unix and mac.
Windows has a function that's called GetTickCount that gets time in millisecond resolution. You can try to work with that, it should be enough to synchronize the time.

Example:
Get time difference between last and current iteration. If it's larger than MILLISECONDS_PER_FRAME, substract MILLISECONDS_PER_FRAME until it's smaller than MILLISECONDS_PER_FRAME, and add 1 to LOGIC_COUNT in each iteration, then set the result to the new last time. If not, keep the current last time.

Then logic loop: Decrement LOGIC_COUNT until it becomes 0, in each iteration advance the games logic by 1 (perform the logic of 1 frame). If LOGIC_COUNT was larger or equal 1, set REDRAW to true.

Finally, display your graphics if redraw is true.

Repeat.

Explanation:
That should cause the games logic to be about equally fast on all systems, unless they are REALLY slow with drawing. There is a logic loop before the graphics refresh because drawing the graphics usually takes longer than the game logic to execute. That's why on slower systems it can happen that the game runs too slow if you execute logic and graphic refreshal at the same rate. On the other hand, this will also make sure the game doesn't run too fast, or redraw the graphics unnecessarily.
Last edited on
To avoid problems with slower machines, one should skip redrawing the scene when the current frame is already behind schedule. That makes sure that the game always runs at the same (logical) speed.
closed account (3pj6b7Xj)
I am not using any libraries, all strictly win32 api.

Yes we both have windows system. I ran an iteration check to see how fast our systems can count in 1 second. Mine counted up to 15,466 on average every second but his machine counts up to 35,788 on average, its almost quadrupple the speed of time! Then again, mine is single core and he has dual core.

I believe I can control the speed of the game by using the system clock to check the seconds variable. If I can do that, I can design the main game loop so that it maintains a certain set of frames every second...wow, it just hit, why didn't I even think of this.

Thanks for the help guys, I'll report back with my results.
Last edited on
mrfaosfx: Using SECONDS is WAY too inaccurate. Calculation time may vary, and we are talking about possible ten thousands of calculations per second. Use the GetTickCount method I suggested if you can't think of anything better, you should definitely NOT use a heuristic based on the average calcs per second. Trust me, it won't work as intended.
closed account (3pj6b7Xj)
I'll give it a try with GetTickCount() then. there is also QueryPerformanceTimer() and QueryPerformanceFrequency() but it gets real messy with the large numbers.
closed account (3pj6b7Xj)
Ok guys so I implemented a timer using GetTickCount() unfortunately it slowed my game down to a crawl, it makes no sense, here is the snippet of the code.

1
2
3
4
5
6
7
8
9
10
11
DWORD dwSnakeTimer; // this is part of the snake class
DWORD dwGrabTick; // also part of the snake class

// the following two are initialized in constructor of snake class.
dwGrabTick=0; // to grab a count from GetTickCount()
dwSnakeTimer=100; // assuming 100 milliseconds

// this code is located inside funciton that updates snakes.
if(!dwGrabTick)dwGrabTick=GetTickCount(); // if 0 set to current tick.
if(GetTickCount()>=dwGrabTick+dwSnakeTimer)dwGrabTick=0; // set to 0 again, continue.
    else return; // Not time yet. 


I also set dwSnakeTimer to 1 and its still SLOW you would think the snakes should update faster but they don't, setting it to 0 makes the snakes super fast. I tried float values using 0.000050 that has no effect since GetTickCount() uses DWORD not float.

I tried other solutions, such as Sleep() but sleep is very innacurate, milliseconds vary from system to system. On my system i tweaked Sleep() to get the speed of the snakes right, when I took over to my friends computer, the Snake was SLOWER than ever...youd think milliseconds would be equal across all systems!? Eventually some count faster than others, that makes no sense.

WM_TIMER behaves the same way as well, I trashed that idea. I also used QueryPerformanceTimer() the count just had a higher resolution and was some what
like GetTickCount() the count varies from system to system.

I have other ideas pending so i'm not giving up just yet.
Last edited on
mrfaosfx:

Here's a thread I wrote on an emu board about one of my previous approaches to this topic:

http://nesdev.parodius.com/bbs/viewtopic.php?p=57398#57398


EDIT:

Just read hanst's solution and that would work also, although it would be more prone to erroneously skipping frames and cause unnecessary jitter.
Last edited on
closed account (3pj6b7Xj)
I will look into this Disch, thanks.
@Disch: Yeah I know it has it's flaws, I suggested it because it's very easy to implement and in this case sufficient IMO. I'll look into your solution aswell though, I'm interested :D
1
2
3
4
// this code is located inside funciton that updates snakes.
if(!dwGrabTick)dwGrabTick=GetTickCount(); // if 0 set to current tick.
if(GetTickCount()>=dwGrabTick+dwSnakeTimer)dwGrabTick=0; // set to 0 again, continue.
    else return; // Not time yet.  


Of course I haven't seen the rest of the code, but if that's your entire code then the problem is of course that it won't cause frame skips on slower machines, which defeats at least half of the point of having a frame limiter in the first place. By just checking wether enough time has passed instead of having a counter for logical frames, you force the program to try to draw AND calculate at your framerate- whereas the point of a frame limiter is to decouple the calculation from the redrawing of the screen, as drawing usually takes more time than the logic calculations. On a machine that can't display 100 fps (which you enforced here) the program will of course suffer from major slow downs as it's so busy updating the screen that it never gets to update the logic.
Last edited on
closed account (3pj6b7Xj)
Here is some code from an experiement I was trying out, it works really well but its really slow and some what innacurate.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if((iDelayCounter++)>iFrameDelay) // iFrameDelay was reached.
            iFrame++,iDelayCounter=0; // increment frame & reset counter.

        if(iSecsPulse!=(int)stSystemTime.wSecond) // a second went by.
        {
            iSecsPulse=(int)stSystemTime.wSecond; // store new second.
            iAverageFrame=iFrame,iFrame=0; // store average & reset frames.
        }

        if(!iFrameCtrl--)
        {
            if(iFPS)continue;
            // adjust iFrameDelay to obtain iFPS.
            if(iAverageFrame<iFPS)iFrameDelay--;
            if(iAverageFrame>iFPS)iFrameDelay++;
            iFrameCtrl=450;
        }


Thanks hanst99 for the info.
You can't use a second timer to regulate anything. It's going to be way to slow.

The thing that makes this tricky is that frames don't take a constant time. What's more, on modern multi-tasking OSes, that's compounded by the fact that you won't even get the same CPU time every frame. What's more, you have to assume whatever timing system you're using has imperfections (because most of them do).

What it looks like you're doing there is saying "okay I waited this much the last second and got X FPS, so if I wait that much in the next second I'll get the same FPS". The problem is that's a false assumption. If you make that assumption the speed of your program will vary wildly depending on how much time your frames occupy, how much CPU time the OS decides to give your program, the phase of the moon, etc.

Really the only way to get a steady framerate without VSyncing is to constantly check the time and see if it's ready for a new frame to render. But even that has pitfalls because if a frame takes longer than usual or if the OS decides not to give your program CPU time for some reason, you'll miss frames.

The approach I posted above (in that thread I linked to) really does a good job of managing all the X factors. You really should be able to drop it into your program with only slight modifications (and please feel free to do so!)

But I can understand if you want to try and come up with your own solution.

EDIT:
Really, though -- my main point was that the track your on with the above attempt is all wrong. You need to rethink it.
Last edited on
closed account (3pj6b7Xj)
I will implement it into the game and see it how it plays out. Thanks again Disch :)
Topic archived. No new replies allowed.