I had a question regarding FPS. I recently adjusted the code of my project so that units could turn and run "identically" on different computers by using a system which ran updates based on 'time' so that FPS did not affect movement (to avoid units moving faster on faster computers). However, in doing this, I found a peculiar difference among 3 different computers, here are their descriptions:
Computer 1: Laptop, pretty old, kinda slow (4 years). 1.73 GHz processor (intel)
Computer 2: Desktop, decent shape, 2-3 years old. 2.01GHz processor (amd)
Computer 3: Desktop, pretty much a beast, recently bought, dual core 3.01 GHz processor (amd)
Now, what is odd about this is that the delta times on Computer 1 are about 5-6 milliseconds. Meaning, it takes the computer to run an entire 'game loop' in this amount of time. On Computer 2, this time is about 2-3 seconds. Now, the SUPER odd thing is you would expect Computer 3 to run these game loops in less time, say 1 millisecond each. However, when we cout these numbers, we consistently get this sequence: "0, 0, 0, 0, x" where x is either 15, or 16 milliseconds. This would mean mean it ran 4 loops in less than a second, and the fifth loop in 15-16 milliseconds.
Note that all of these numbers increase when we slow the computers down by adding in a bunch of models into the view of the camera. However, this does not prevent Computer 3 from changing its pattern, it will just go "0, 0, 0, 31" for instance.
The reason this is such a huge issue is that the whole theory behind delta time assumes that faster computers will give u smaller delta time numbers, so you can usually depend on that during your calculations. But with these fluctuating values (a slow computers being high, a decent computer being low, and a really fast computer being REALLY high), I cannot write a formula for my movements which would be "fair" to all computers.
Finally, I think this may have to due with Computer 3 being a dual core processor. Any insight on the situation would be greatly appreciated.
Here is the code of our main:
Code: Select all
int main()
{
// Create everything needed for the basic game engine to run. If any of these fail, nothing will be able to continue.
// We run a check at the end to make sure everything loaded properly.
IrrlichtDevice* device = createDevice(EDT_OPENGL, dimension2d<s32>(1280, 1024), 16, false);
//device->getFileSystem()->changeWorkingDirectoryTo("C:/Frontier/"); //Sets the working Directory
IVideoDriver* driver = device->getVideoDriver();
ISceneManager* smgr = device->getSceneManager();
IGUIEnvironment* env = device->getGUIEnvironment();
ISoundEngine* engine = createIrrKlangDevice();
// The returned timer is automatically created when you create a device.
ITimer* timer = device->getTimer();
if (device == 0 || driver == 0 || smgr == 0 || env == 0 || engine == 0)
return 1; // 1 indicates that something went wrong with one of these classes.
// Look into Exceptions so that if this occurs to a player, we can print useful information to the screen.
// This tells the video driver to draw 32bit images when drawing textures.
driver->setTextureCreationFlag(ETCF_ALWAYS_32_BIT, true);
// This tells the video driver to create mip-maps while drawing textures.
driver->setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, true);
Manager manager(device, engine, smgr, driver, env); // Create the event receiver
device->setEventReceiver(&manager); //Set the event receiver, this MUST stay constant throughout the program.
manager.onLoad(); // Initializes everything needed for the beginning of the program.
// All of these variables are used to control the speed of the things in the game, NOT the FPS.
// We have made the two indepedent of each other so the game plays almost identical on
// faster and slower computers.
int frameCounter = 0;
double deltaTime;
double oldTime = timer->getTime();
double timeOfLastLoop = timer->getTime();
double timeDifference; // This is only used to make the math a little more readable where it is used.
double currentTime;
ITexture * mouseTexture = driver->getTexture("media/hand.tga");
position2d<s32> mouseAdjustment;
mouseAdjustment.X = device->getCursorControl()->getPosition().X + 1;
mouseAdjustment.Y = device->getCursorControl()->getPosition().Y + 1;
IGUIImage * mouse = env->addImage(mouseTexture, mouseAdjustment);
device->getCursorControl()->setVisible(false);
// This is the 'Game Loop' as long as it runs, the game will continually draw and redraw everything on the screen.
// In almost all cases, this loop is not changed directly. The only way the game changes is through the event receiver.
while(device->run())
{
if (device->isWindowActive())
{
deltaTime = timer->getTime() - timeOfLastLoop;
cout<< deltaTime << endl;
mouseAdjustment.X = device->getCursorControl()->getPosition().X + 1;
mouseAdjustment.Y = device->getCursorControl()->getPosition().Y + 1;
if(mouse != 0)
{
mouse->remove();
mouse = 0;
}
if(!manager.getRButtonIsDown())
{
mouse = env->addImage(mouseTexture, mouseAdjustment);
}
// Allows the game to be 'unpaused' and restarts the game timer when the window becomes active.
// See the 'else' statement below for clarity.
if (timer->isStopped())
timer->start();
// Record the time between now and the previous loop
// This allows the manager to know what that time is so it may use it in its calculations.
// However, with really fast computers, often, deltaTime will be 0 since the 'Game Loop'
// is running so fast. This check prevents jagged movement when deltaTime is 0.
if(deltaTime > 0)
{
manager.setDeltaTime(deltaTime);
manager.updateKeyInput(); //Updates the game based on any key inputs.
manager.updateGameplay();
manager.checkDistances();
}
// This allows the FPS to be displayed every time we have gone through 500 frames.
if(frameCounter == 500)
{
currentTime = timer->getTime(); // Tag the time since this "time" will be used in two different
timeDifference = currentTime - oldTime; // calculations. This allows us to not call getTime() twice,
oldTime = currentTime; // which, if we did, would cause us to "lose" milliseconds in
// our calculations.
manager.updateFPS(frameCounter / timeDifference * MILISECONDS_PER_SECOND);
frameCounter = 0; // reset the frame counter
}
driver->beginScene(true, true, 0);
// Anything you want to see BEFORE the screen updates (per loop), must be inserted BEFORE smgr->drawAll();
smgr->drawAll();
manager.updateAspects();
// Any GUI aspects that need to be seen BEFORE the screen updates (per loop), must be inserted BEFORE env->drawAll();
env->drawAll();
// This tells the video driver to finish drawing the scene, else we would never be able to continue.
driver->endScene();
frameCounter++;
timeOfLastLoop = timer->getTime();
}
else
{ // This allows us to pause the game and not have it 'accelerate' when tabbed out of, then back into by stopping the virtual timer from updating.
if (!timer->isStopped())
timer->stop();
}
}
// This ends the program and exits. As a convention, main is always of 'int' type and therefore must return a value.
// We return 0 to indicate that the program ran to completion.
// Any other error would return another Counter(i.e. return 1 that we saw above) for debugging purposes.
device->drop();
return 0;
}
Note that the (if deltaTime > 0) did NOT affect the values for computer 3, or any computer for that matter.