Thread: Is this a good framerate method?

  1. #1
    "The Oldest Member Here" Xterria's Avatar
    Join Date
    Sep 2001
    Location
    Buffalo, NY
    Posts
    1,039

    Is this a good framerate method?

    basically, to put down all the people who complain that when they play my game everything goes either extremely slow or fast, i've come up with my own method(state of the art) to make the speeds more sensible for all computers, slow speeds or fast.

    what i have is a timer that waits five seconds before the game(displaying 'Loading' to the user) and within that five seconds is a variable (x for instance) that is getting incremented.

    Then after the timer the program looks at how many increments it has done. If it's over 2 million, i'll make the game speed slower(like the sprites and stuff) so it can suit a faster running computer so everything doesnt race across the screen really fast. And say if x is under 100,000 i'll make everything move in larger increments so that it doesn't seem so slow on slower running computers.

    OBSERVE
    Code:
    while(timersnotdoneyet)
    {
         x++;
    }
    if(x > 2000000)
    #define FAST_COMP
    
    if(x > 1000000 && x < 2000000)
    #define MEDI_COMP //medium-speed comp
    
    if(x < 1000000){
    #define SLOWASS_COMP
    MsgBox(NULL,"Your comp sux0rz! Get a job");
    }
    now when the user moves the guy around

    Code:
    if(GetAsyncKeyState(VK_RIGHT)){
    #ifdef FAST_COMP
    PlayerX += 0.01f;
    #endif
    
    #ifdef MEDI_COMP
    PlayerX += 0.1f;
    
    #ifdef SLOWASS_COMP
    PlayerX ++;
    }
    Does this sound like a reasonable way to control the game speed so it doesn't go out of control? Or is there some very simple framerate principle that i'm not getting?

    thanks!

  2. #2
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Code:
    void GameLoop
    {
       static float last_time=(float)timeGetTime();
    
       do
       {
           float current_time=(float)timeGetTime();
           float time_delta=(current_time-last_time)*0.001f;
           last_time=current_time;
           UpdateGame(time_delta);  
           ...
           ...
        } while (ingame);
    
    }

    Then you simply increment all your objects by object.speed*time_delta and everything will run the same speed on all systems.

  3. #3
    l'Anziano DavidP's Avatar
    Join Date
    Aug 2001
    Location
    Plano, Texas, United States
    Posts
    2,743
    i love your last macro definition
    My Website

    "Circular logic is good because it is."

  4. #4
    'AlHamdulillah
    Join Date
    Feb 2003
    Posts
    790
    Then you simply increment all your objects by object.speed*time_delta and everything will run the same speed on all systems.
    that is pretty good way to do things in the beginning; however, variable time-steps are a really f-ed up way to do things if you are planning on implementing physics eventually(I presume you are), because variable time steps cause the problem of randomness in actions(basically, for different computers, the physics will update at different times, thereby removing the possibility that everything will always be the same. For example, a fast computer might get 200 physics update loops performed a second, while a slow one will only get 10-15. This causes a huge "jump" if you will because the updates will be slower.) . A fixed-timestep IMO is a much better way of doing things(I am not bashing you bubba, I am just trying to give an alternative, and IMO, better way of implementing a timebased update system).

    I would suggest some derivation of the following(I give credit to some on gamedev for this)

    Code:
    float AccumTime = 0;
    
    void physics()
    {
    
      float timedelta = getdeltatime(); // grab time between last call
    
      for(AccumtTime += timedelta; AccumTime > timestep; AccumTime -= timestep)
             {
                 UpdatePhysics(); // now all of your physics functions can have 1 speed.
             }
    }
    then it is quit simple to determine physics/rendering update times by changing the val of timestep. say, if you want to go 4x faster, then timestamp = timestamp/4. This also allows you to merely change a single variable and get updates, instead of passing deltatime to all of your functions.
    Last edited by EvBladeRunnervE; 07-05-2004 at 08:38 AM.
    there used to be something here, but not anymore

  5. #5
    Registered User
    Join Date
    Aug 2001
    Posts
    411
    Quote Originally Posted by EvBladeRunnervE
    A fixed-timestep IMO is a much better way of doing things.
    So, on one computer it looks fine, but on a faster computer it runs faster... a slower computer it runs slower.

    Perhaps I am missing the point.

  6. #6
    Crazy Fool Perspective's Avatar
    Join Date
    Jan 2003
    Location
    Canada
    Posts
    2,640
    Quote Originally Posted by Eber Kain
    So, on one computer it looks fine, but on a faster computer it runs faster... a slower computer it runs slower.

    Perhaps I am missing the point.
    The time steps are fixed. If you are using a fast computer, then deltaTime will be small. the loop only executes if the accumulated time is greater than the time step, so, on a fast computer you may need to call be function a few times before it executes once. On a slow computer, deltatime will be large meaning the loop may execute multiple times for one call to the function. This is what balences the updates from fast to slow computers.

    very interesting EvBlade

  7. #7
    S Sang-drax's Avatar
    Join Date
    May 2002
    Location
    Göteborg, Sweden
    Posts
    2,072
    To answer your title question and to paraphrase Office Space: It's a horrible, horrible idea, XTerria.

    There are much, much better ways to do this (like the one Bubba suggested).
    Your #defines won't work either. Remember, #defines are evaluated compile-time before everything else. You cannot define macros within if-statements and expect that it will work as expected.
    Last edited by Sang-drax : Tomorrow at 02:21 AM. Reason: Time travelling

  8. #8
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    That code is not meant for the physics engine but unless you are getting into some serious physics and rigid body simulations I doubt that my code will cause that much concern.

    Physics simulation and the time steps involved were not what I was addressing here and I sorta thought that was beyond the scope of this thread so I deferred its discussion.

    But Ev is right...that won't work as well with physics as it does with geometry, frames per second calcs, etc.

    Also the way that Ev showed how to do physics is also how you count frames per second.

    I left that out of the code for clarity's sake.

  9. #9
    "The Oldest Member Here" Xterria's Avatar
    Join Date
    Sep 2001
    Location
    Buffalo, NY
    Posts
    1,039
    Quote Originally Posted by Bubba
    Code:
    void GameLoop
    {
       static float last_time=(float)timeGetTime();
    
       do
       {
           float current_time=(float)timeGetTime();
           float time_delta=(current_time-last_time)*0.001f;
           last_time=current_time;
           UpdateGame(time_delta);  
           ...
           ...
        } while (ingame);
    
    }

    Then you simply increment all your objects by object.speed*time_delta and everything will run the same speed on all systems.

    thanks but there is a very large flaw in that. Not only the physics, but there is no clear-cut way of telling the user that his or her computer sux0rz. Unfortunatly this will cause problems in the future when i'm heavily adding on to my engine.


    lol no i'm just kidding thanks alot! i'll use that method.

  10. #10
    'AlHamdulillah
    Join Date
    Feb 2003
    Posts
    790
    very interesting EvBlade
    well, I grabbed it from one of the forum threads @ gamedev.net's forums about physics, and modified the variable/functions names, so the credit goes to the helpful people @ gamedev.net.

    Physics simulation and the time steps involved were not what I was addressing here and I sorta thought that was beyond the scope of this thread so I deferred its discussion.
    I agree it is beyond the scope of this discussion, but I was hoping that by mentioning fixe-timestep methods, we may save XTerria a question when he moves on to implementing a physics system and wonders why things operate "differently" on different computers with variable time-steps.

    Also, it helps do wonders when it comes to networking a game's math/physics with another instance ... for reasons that are pretty obvious
    there used to be something here, but not anymore

  11. #11
    Banned
    Join Date
    May 2004
    Posts
    129
    As a word of warning, timeGetTime is definitely not the best way of calculating framerates. It will work, but you will also notice weird things with it. This is because it only has a precision of about 1ms, and yes, that does screw with things. Just a word of warning.

    So, on one computer it looks fine, but on a faster computer it runs faster... a slower computer it runs slower.

    Perhaps I am missing the point.
    If you are running a physics frame every time you run a graphics frame, then you will have computer A running at 200fps, and computer B running at 50fps. The obvious problem is that the computer running at the lower framerate has a larger timestep, and subsequently its approximation of the actual position of various objects in the world is not as good as the one running at 200fps. The other, less obvious, but ultimately much more drastic problem is that because computer A is running at 200fps, the small floating point inaccuracies innate to BOTH computers is applied MORE on computer A. Subsequently, in games such as Quake3, computers running at ridiculously high framerates allow the player of that computer to jump higher than the slower computers.

    The solution for this is to define a frequency to run a physics frame. Say you are using 100Hz. This means that until the graphics framerate reaches 100FPS, you can run at least one physics frame per graphics frame (at 50 FPS you can run 2 physics frames, etc). This means that the time between each physics frame is a constant timestep, and the errors pointed out before are snuffed out (your estimations of the real position of objects are going to have a certain amount of error, but if that error is the same for EVERYTHING then it is essentially okay). This also means you cannot run a framerate of more than 100FPS. Doom3 uses 60Hz for physics (so don't expect a framerate that is any faster).

  12. #12
    Registered User
    Join Date
    Jul 2004
    Posts
    3
    Here's some source code I wrote for timers in an engine I made. The engine used SDL and so the class had support for SDL. This can be easily removed.

    Timer.h:

    Code:
    #if defined(_MSC_VER) || defined(_BORLANDC_)
    #define u64  __int64
    #define WINDOWS
    #elif defined(__LINUX__) || defined(__MACOSX__)
    #define u64 unsigned long long
    #define LINUX
    #elif defined(__MINGW32__) 
    #define u64 unsigned long long
    #define WINDOWS
    #endif
    
    class CTimer {
    public:
    	CTimer();
    	CTimer(unsigned int type);
    	CTimer(CTimer& oldTimer);
    
    	~CTimer() { }
    
    	void  SetTimerType(unsigned int newType);
    	unsigned int GetTimerType();
    
    	void SetUpdateInterval(float newInterval);
    	float GetUpdateInterval();
    
    	void  Reset();
    
    	float GetFPS();
    	float GetTime();
    private:
    	void  InitTimer();
    
    	float  timeAtStart, lastUpdate, updateInterval, fps;
    	u64    ticksPerSecond;
    	unsigned int  timerType, frames;
    };
    
    enum Timers {
    	Z_TIMER_SDL,	//Uses SDL_GetTicks() for time
    	Z_TIMER_QPC,	//Uses QueryPerformanceCounter() for time (Windows Only)
    };
    Timer.cpp:

    Code:
    #include "Timers.h"
    #include <SDL.h>
    #if defined (WINDOWS)
    #include <afxwin.h>
    #endif
    
    CTimer::CTimer() : timerType(Z_TIMER_SDL) {
    	//We Default To SDL Timers, Then Reset Timers
    	Reset();
    }
    
    CTimer::CTimer(unsigned int type) {
    	timerType = Z_TIMER_SDL;
    	timerType = type;
    	Reset();
    }
    
    CTimer::CTimer(CTimer& oldTimer) {
    	//Copy Old Data Into This Class
    	timerType      = oldTimer.timerType;
    	timeAtStart    = oldTimer.timeAtStart;
    	ticksPerSecond = oldTimer.ticksPerSecond;
    	timerType      = oldTimer.timerType;
    	lastUpdate     = oldTimer.lastUpdate;
    	fps            = oldTimer.fps;
    	updateInterval = oldTimer.updateInterval;
    
    
    	//Do Timer Initialization
    	InitTimer();
    }
    
    void CTimer::SetTimerType(unsigned int newType) {
    	if(newType != Z_TIMER_SDL || newType != Z_TIMER_QPC)
    		newType = Z_TIMER_SDL;
    	timerType = newType;
    	timerType = Z_TIMER_SDL;
    
    	//Initialize The Timer
    	InitTimer();
    }
    
    unsigned int CTimer::GetTimerType() {
    	return (timerType);
    }
    
    void CTimer::SetUpdateInterval(float newInterval) {
    	updateInterval = newInterval;
    }
    
    float CTimer::GetUpdateInterval() {
    	return updateInterval;
    }
    
    float CTimer::GetFPS() {
    	frames++;
    	float currentUpdate = GetTime();
    	
    	if(currentUpdate - lastUpdate > updateInterval) {
    		fps = frames / (currentUpdate - lastUpdate);
    		lastUpdate = currentUpdate;
    		frames = 0;
    	}
    
    	return (fps);
    }
    
    void CTimer::Reset() {
    	timeAtStart    = 0;
    	ticksPerSecond = 0;
    	frames         = 0;
    	lastUpdate     = 0;
    	fps            = 0;
    	updateInterval = 0.5;
    
    	InitTimer();
    	timeAtStart = GetTime();
    }
    
    void CTimer::InitTimer() {
    	if(timerType == Z_TIMER_QPC) {
    		//We Only Need To Do Initialization For the QPC Timer
    		//We Need To Know How Often The Clock Is Updated
    		if( !::QueryPerformanceFrequency((LARGE_INTEGER*)&ticksPerSecond))
    			ticksPerSecond = 1000;
    	}
    }
    
    float CTimer::GetTime() {
    	if(timerType == Z_TIMER_SDL) {
    		return ((float)SDL_GetTicks()/1000.0f);
    	} 
    	else if(timerType == Z_TIMER_QPC) {
    		u64    ticks;
    		float  time;
    
    		//This is the number of clock ticks since the start
    		if( !::QueryPerformanceCounter((LARGE_INTEGER*)&ticks))
    			ticks++;
    
    		//Divide by frequency to get time in seconds
    		time = (float)(u64)ticks / (float)(u64)ticksPerSecond;
    
    		//Calculate Actual Time
    		time -= timeAtStart;
    
    		return (time);
    	}
    	return (0.0f);
    }

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. stuck on display method
    By shintaro in forum C++ Programming
    Replies: 2
    Last Post: 02-01-2009, 05:17 PM
  2. Best communication method to thousand childs?
    By Ironic in forum C Programming
    Replies: 8
    Last Post: 11-08-2008, 12:30 AM
  3. C# method
    By siten0308 in forum C# Programming
    Replies: 6
    Last Post: 07-15-2008, 07:01 AM
  4. Overriding a method in C
    By DavidDobson in forum C Programming
    Replies: 1
    Last Post: 07-05-2008, 07:51 AM
  5. Need a good method
    By Asagohan in forum Tech Board
    Replies: 11
    Last Post: 04-08-2005, 04:57 PM