mechacrash
Level 1
It's almost like I'm actually being productive!
|
|
« on: August 03, 2010, 11:54:43 AM » |
|
I am having alot of problems with game loops. I want to be able to support a user specified FPS however due to rounding and my method of interval calculation, its impossible to use FPS that do not make a whole number when 1000 is divided by them (e.g. 60fps -> 1000 / 60 = 16.66667)
i cant think of any other methods of getting a stable and variable game loop... any suggestions or help?
below is a simple example file... to use it you need to include the winmm library (-lwinmm in G++)
#include <iostream> #include <windows.h>
using namespace std;
int main(){
timeBeginPeriod(1); unsigned tick = 0; unsigned ticka = timeGetTime(); unsigned ms = 0; unsigned fpscount = 0; float update = 1000.0 / 50.0; // <- change 50.0 to your desired FPS while(1){ ms = timeGetTime(); if(ms < tick || ms - tick >= (unsigned)update){ tick = ms; fpscount++; } if((ticka / 1000) != (timeGetTime() / 1000)){ ticka += 1000; cout << fpscount << "\n"; fpscount = 0; } } timeEndPeriod(0); return 0; }
|
|
|
Logged
|
|
|
|
Average Software
|
|
« Reply #1 on: August 03, 2010, 11:59:30 AM » |
|
On Windows, try using the Sleep function. My game loop does a Sleep(6); every iteration, which gets me roughly 160 FPS. You can play with this value to vary the framerate. // Event loop. while (true) { // See if there's a message. if (PeekMessage(&message, NULL, 0, 0, PM_REMOVE)) { // If it's a quit message, bail. if (message.message == WM_QUIT) { break; } // Dispatch the message. TranslateMessage(&message); DispatchMessage(&message); } else { // If there are no messages, call the main game loop. MainWindow::CallMainLoop();
// Sleep for a bit to avoid pegging the processor. Sleep(6); } }
|
|
|
Logged
|
What would John Carmack do?
|
|
|
increpare
Guest
|
|
« Reply #2 on: August 03, 2010, 12:03:24 PM » |
|
Perfectly stable FPS has to be a divisor of monitor refresh-rate (yeah yeah threading I know I know). So arbitrary ones are going to be impossible anyway. So don't worry about getting it exact.
|
|
« Last Edit: August 03, 2010, 12:24:22 PM by increpare »
|
Logged
|
|
|
|
mechacrash
Level 1
It's almost like I'm actually being productive!
|
|
« Reply #3 on: August 03, 2010, 12:18:34 PM » |
|
but then how do people hit 60fps? i sortve made a method of doing it by running it at 16 milliseconds intervals and at every third frame going for a 17 millisecond interval... they evened each other out and gave me a steady 60fps... but it wasnt perfect and it didnt work for other values...
also "average software" i need to attempt to lock it to a specific FPS, hence the stupid function calls... using a sleep timer is quite bad in my case because the FPS would change depending on OS, hardware... etc.
i only use sleep to stop bogging the CPU down... which i didnt put in the example code as im lazy
|
|
|
Logged
|
|
|
|
Average Software
|
|
« Reply #4 on: August 03, 2010, 12:29:18 PM » |
|
but then how do people hit 60fps? i sortve made a method of doing it by running it at 16 milliseconds intervals and at every third frame going for a 17 millisecond interval... they evened each other out and gave me a steady 60fps... but it wasnt perfect and it didnt work for other values...
also "average software" i need to attempt to lock it to a specific FPS, hence the stupid function calls... using a sleep timer is quite bad in my case because the FPS would change depending on OS, hardware... etc.
i only use sleep to stop bogging the CPU down... which i didnt put in the example code as im lazy
60 FPS most likely comes from using vertical sync on a monitor with a 60Hz (or multiple) refresh rate. Without that, you need to do lots of timer tricks, including timing how long your loop takes to execute and doing delay calculations on the fly. I've gone down this route before, and came to the conclusion that it simply isn't worth it. Now I just use sleep functions to hit something around 150 FPS and give the user the option to enable vertical sync. Delta time takes care of the rest.
|
|
|
Logged
|
What would John Carmack do?
|
|
|
mechacrash
Level 1
It's almost like I'm actually being productive!
|
|
« Reply #5 on: August 03, 2010, 12:32:30 PM » |
|
so... erm... how do i do Vsync?
also isnt that a fullscreen function only?
|
|
|
Logged
|
|
|
|
tempestad
|
|
« Reply #6 on: August 03, 2010, 12:40:01 PM » |
|
SFML has that built on it, here is that part on the source code: http://www.sfml-dev.org/documentation/1.6/Window_8cpp-source.htm#l00353You have to calculate the remaining time to get the frame rate needed, and then wait that time. The variable myFramerateLimit is the framerate, in your example. MyClock.Reset() sets the clock to 0, then when you call MyClock.GetElapsedTime() you get the time (in seconds!) from the last Reset(). I think that should be enough to understand how to do it Edit: Remember that SFML uses time in seconds with a float type, so if you use milliseconds adapt as necessary.
|
|
« Last Edit: August 03, 2010, 12:51:30 PM by tempestad »
|
Logged
|
|
|
|
Average Software
|
|
« Reply #7 on: August 03, 2010, 01:01:56 PM » |
|
so... erm... how do i do Vsync?
also isnt that a fullscreen function only?
I depends on how you're doing your graphics. I know how to do it for OpenGL, if you're using DirectX or something else, I can't help you. And no, it isn't fullscreen only. For OpenGL on Windows, you need to get a pointer to the wglSwapInterval function, which is an extension, this code will do that: // Get the vertical sync extension. swap_interval = reinterpret_cast<BOOL (APIENTRY*)(int)>(wglGetProcAddress("wglSwapIntervalEXT")); Once you have the pointer, calling it with a parameter of 0 disables vertical sync, 1 enables it. Other values may have other effects, but I don't know them.
|
|
|
Logged
|
What would John Carmack do?
|
|
|
tempestad
|
|
« Reply #8 on: August 03, 2010, 01:07:49 PM » |
|
VSync will give you different framerates depending on the refresh rate of the monitor. In this case you would need to apply timing to the gameplay.
|
|
|
Logged
|
|
|
|
mechacrash
Level 1
It's almost like I'm actually being productive!
|
|
« Reply #9 on: August 03, 2010, 03:46:08 PM » |
|
hmmm... ok... ill look at vsync then
i just originally assumed that vsync was for binding the drawing updates to the refresh rate... wheras my current implementation updates the drawing continuously through the while loop and the 60FPS was meant to be used to update the game logic 60 times a second...
am i on track?
|
|
|
Logged
|
|
|
|
Average Software
|
|
« Reply #10 on: August 03, 2010, 04:28:10 PM » |
|
i just originally assumed that vsync was for binding the drawing updates to the refresh rate... wheras my current implementation updates the drawing continuously through the while loop and the 60FPS was meant to be used to update the game logic 60 times a second...
Vsync is for binding to the refresh rate. My point was that most programs I've seen that peg 60 FPS do it by switching to a 60Hz display mode and using vsync. Is there any particular reason why you want the player to be able to set the framerate? Seems like an odd sort of feature. I think no matter what you do, it's going to be rough estimate.
|
|
|
Logged
|
What would John Carmack do?
|
|
|
mechacrash
Level 1
It's almost like I'm actually being productive!
|
|
« Reply #11 on: August 03, 2010, 10:08:29 PM » |
|
well im currently developing a small engine for use with a handful of projects i want to persue... so i wanted to make different aspects of the code customisable so i wouldnt have to rewrite code later... FPS was one o fthose things
|
|
|
Logged
|
|
|
|
|
dcarrigg
|
|
« Reply #13 on: August 04, 2010, 08:01:51 PM » |
|
Hey mechacrash, If you're looking to support any specified framerate, I wouldn't go about it by using vsync. It may work correctly, but not only is vsync something that drivers can override from your application's preference, but you probably dont want to have the rendering portion of your engine in control of how often your game loop is processed. The first link here is a great article by Glenn Fiedler, but its discussing decoupling physics updates from main game loop updates and doesnt really talk about doing a fixed timestamp for the main game loop. However, the same methodology that he uses to ensure a fixed time stamp for his physics updates, you can use for your main game loop. Applied to the main game loop, it would follow a pattern like this: unsigned int lastTickTime = getTicks(); // This can be whatever high precision timer you are using unsigned int accumulator = 0; unsigned int numTicksPerUpdate = 16; // 16 = 1/60fps. 32 = 1/30fps, while (!done) { unsigned int currentTickTime = getTicks(); unsigned int timePassed = currentTickTime - lastTimeTime; lastTickTime = currentTickTime;
accumulator += timePassed;
// Now, if your game hitches, if you want it to call multiple updates to make up for the lost time, use this while (accumulator > numTicksPerUpdate) { accumulator -= numTicksPerUpdate; // MAIN GAME LOOP LOGIC }
// Otherwise, when your game hitches, if you want it to call a single update and continue, dropping the missed time, use this if (accumulator > numTicksPerUpdate) { accumulator = 0; // MAIN GAME LOOP LOGIC } }
I just whipped this up right now, but I THINK it should handle whatever update interval you'd like. Let me know if you have any questions.
|
|
|
Logged
|
|
|
|
deWiTTERS
|
|
« Reply #14 on: August 05, 2010, 02:33:11 AM » |
|
Some time ago I wrote an article about game loops, so you might find some more info there. It discusses various implementation and their pro's & con's. There is even a lecture video of a guy who used my article as a reference .
|
|
|
Logged
|
|
|
|
zacaj
|
|
« Reply #15 on: August 05, 2010, 06:15:45 AM » |
|
int start=clock(); //game stuff while(clock()-start<CLOCKS_PER_SEC/FPS_DESIRED); thats a really simple way that seems to work fine, it might be >, not < though
|
|
« Last Edit: August 05, 2010, 06:59:14 AM by zacaj »
|
Logged
|
My twitter: @zacaj_Well let's just take a look at this "getting started" page and see-- Download and install cmake
Noooooooo
|
|
|
Average Software
|
|
« Reply #16 on: August 05, 2010, 07:18:13 AM » |
|
int start=clock(); //game stuff while(clock()-start<CLOCKS_PER_SEC/FPS_DESIRED); thats a really simple way that seems to work fine, it might be >, not < though Except that busy loops like that keep the process running at maximum. Laptop users especially don't like that, makes CPUs really hot and kills batteries pretty quickly. Even the fans in my desktop system get really ramped up when I do that.
|
|
|
Logged
|
What would John Carmack do?
|
|
|
zacaj
|
|
« Reply #17 on: August 05, 2010, 07:43:50 AM » |
|
Yea, and unless theres some c function that takes ms, not seconds like sleep(), that will happen no matter what. Also, I never had my programs using up all my CPU, and my computer shuts off it its a hot day
|
|
|
Logged
|
My twitter: @zacaj_Well let's just take a look at this "getting started" page and see-- Download and install cmake
Noooooooo
|
|
|
Average Software
|
|
« Reply #18 on: August 05, 2010, 08:00:45 AM » |
|
Yea, and unless theres some c function that takes ms, not seconds like sleep(), that will happen no matter what.
OS APIs typically provide such a function. Sleep(), (capital S) on Windows, which I pointed out earlier.
|
|
|
Logged
|
What would John Carmack do?
|
|
|
zacaj
|
|
« Reply #19 on: August 05, 2010, 08:28:26 AM » |
|
nothing cross platform though...
|
|
|
Logged
|
My twitter: @zacaj_Well let's just take a look at this "getting started" page and see-- Download and install cmake
Noooooooo
|
|
|
|