[SDL] How to use ticks and have a constant frame rate

Started by
2 comments, last by Joe_Slap 18 years, 5 months ago
Hi everybody, I'm trying to work on my humble little project and I need some info about how to use ticks for animations with SDL. I'm a newbie and I tried to realize a spite animation (a character that walks to north, south, east and west) in a very primitive (and useless) way, it works badly because I don't use ticks, I think. The problem is that it doesn't have a constant frame rate and it directly depends on cpu speed and memory occupation (you load something while running the animation, it slows down). I'll show you my horrible code (remember I'm very newbie), I stored all the anim frames in 4 arrays (one for each direction, rx, lx, dw, up), is it correct? How to transform this animation using ticks so that it'll always have a constant frame rate? Ah, it is supposed to be a point and click interface so you have to point with your mouse and the character should reach the signed destination. Thanks a lot guys. :)
 
#include <stdio.h>
#include <stdlib.h>
#include <SDL.h>
#include <SDL_image.h>

void DrawImage(SDL_Surface *img, SDL_Surface *dest, int x, int y)
{
    SDL_Rect rect;
    rect.x = x;
    rect.y = y;
    rect.h = 50;
    rect.w = 60;
    SDL_BlitSurface (img, NULL, dest, &rect);
}


int main(int argc, char *argv[])
{
	// Inizializzo SDL
	if(SDL_Init(SDL_INIT_VIDEO) == -1)
    {
		fprintf(stderr, "Failed to initialize SDL: %s\n", SDL_GetError());
		exit(1);
    }
	
    // pointers for all the directions
	SDL_Surface *rx[8];
	SDL_Surface *lx[8];
	SDL_Surface *up[8];
	SDL_Surface *dw[8];
	SDL_Surface *frame;
	
    rx[0] = IMG_Load("PG/right_0001.png");
    rx[1] = IMG_Load("PG/right_0002.png");
    rx[2] = IMG_Load("PG/right_0003.png");
    rx[3] = IMG_Load("PG/right_0004.png");
    rx[4] = IMG_Load("PG/right_0005.png");
    rx[5] = IMG_Load("PG/right_0006.png");
    rx[6] = IMG_Load("PG/right_0007.png");
    rx[7] = IMG_Load("PG/right_0008.png");
    lx[0] = IMG_Load("PG/left_0001.png");
    lx[1] = IMG_Load("PG/left_0002.png");
    lx[2] = IMG_Load("PG/left_0003.png");
    lx[3] = IMG_Load("PG/left_0004.png");
    lx[4] = IMG_Load("PG/left_0005.png");
    lx[5] = IMG_Load("PG/left_0006.png");
    lx[6] = IMG_Load("PG/left_0007.png");
    lx[7] = IMG_Load("PG/left_0008.png");
    up[0] = IMG_Load("PG/rear_0001.png");
    up[1] = IMG_Load("PG/rear_0002.png");
    up[2] = IMG_Load("PG/rear_0003.png");
    up[3] = IMG_Load("PG/rear_0004.png");
    up[4] = IMG_Load("PG/rear_0005.png");
    up[5] = IMG_Load("PG/rear_0006.png");
    up[6] = IMG_Load("PG/rear_0007.png");
    up[7] = IMG_Load("PG/rear_0008.png");
    dw[0] = IMG_Load("PG/front_0001.png");
    dw[1] = IMG_Load("PG/front_0002.png");
    dw[2] = IMG_Load("PG/front_0003.png");
    dw[3] = IMG_Load("PG/front_0004.png");
    dw[4] = IMG_Load("PG/front_0005.png");
    dw[5] = IMG_Load("PG/front_0006.png");
    dw[6] = IMG_Load("PG/front_0007.png");
    dw[7] = IMG_Load("PG/front_0008.png");
    
 	atexit(SDL_Quit);
	
	SDL_Surface *screen = SDL_SetVideoMode(640,480,32,SDL_HWSURFACE|SDL_DOUBLEBUF);
 	
	if(screen == NULL){
		fprintf(stderr, "Unable to set video mode: %s\n", SDL_GetError());
		exit(1);
	}
	
	int isRunning = 1;
	int animation = 0;
	int i=0;
	int s=0;
	int e=6;
	int X=0;
	int Y=220;
	int targetX=-500;
	int targetY=0;
	char current='X';
	char dir='R';
	
	// Creo un loop per tenere sempre la finestra attiva in visualizzazione	
	while(isRunning)
    {
        if (animation == 1)
        {
               if (current!='Y')
               {
                  int appx = (X+67) - targetX;
                  if (appx != 0)
                  {
                      if (appx < 0)
                      {
                          dir='R';
                          X = X+1;
                      }    
                      else
                      {
                          dir='L';
                          X = X-1;
                      }
                  }
                  else
                  {
                      current='Y';
                  }        
               }    
               else
               {
                  int appy = (Y+270) - targetY;
                  if (appy != -270 && appy != 0)
                  {
                      if (appy < 0)
                      {
                          dir='B';
                          Y = Y+1;
                      }    
                      else
                      {
                          dir='T';
                          Y = Y-1;
                      }
                  }
                  else
                  {
                      animation = 0;
                      current='X';
                  }        
               }                              
            
           //temporize frames
           if (s < e)
           {
              s = s +1;
           }
           else
           {
              s = 0;
              //print frame
              if (i > 5)
              {
                 i = 0;
              }
              else
              {
                 i = i + 1;               
              }   
           }
           
            switch(dir)
            {  
            case 'R':
                 frame = rx;
                 break;
            case 'L':
                 frame = lx;
                 break;
            case 'T':
                 frame = up;
                 break;
            case 'B':
                 frame = dw;
                 break;            
            }      
        }    
        else
        {
            switch(dir)
            {  
            case 'R':
                 frame = rx[0];
                 break;
            case 'L':
                 frame = lx[0];
                 break;
            case 'T':
                 frame = up[0];
                 break;
            case 'B':
                 frame = dw[0];
                 break;            
            }   
        }           
        
	    // Check if player quits
		SDL_Event event;
		while(SDL_PollEvent(&event))
        {
			switch(event.type)
            {  
            case SDL_MOUSEBUTTONDOWN:
                  switch(event.button.button)
                  {
                      case SDL_BUTTON_LEFT:
                           targetX = event.button.x;
                           targetY = event.button.y;
                           animation = 1;
                           current='X';
                           break;
                  }    
                  break;
                  
			case SDL_QUIT:
				isRunning = 0;
				break;   
		    case SDL_KEYDOWN:
                switch(event.key.keysym.sym)
                {                
    			case SDLK_ESCAPE:
    				isRunning = 0;
    				break;
			   	case SDLK_RIGHT:
    				break;
			   	case SDLK_LEFT:
    				break;
    			}
  			break;
			case SDL_KEYUP:
                switch(event.key.keysym.sym)
                {
			   	case SDLK_RIGHT:
    				break;
			   	case SDLK_LEFT:
    				break;
    			}
			}
		}
		
		SDL_FillRect(screen, NULL, 0);
		DrawImage(frame,screen,X,Y);   
		SDL_Flip(screen);
	}
	
	SDL_FreeSurface(rx[8]);
	SDL_FreeSurface(lx[8]);
	SDL_FreeSurface(up[8]);
	SDL_FreeSurface(dw[8]);
	
}


Advertisement
I don't know the details of SDL, but in general, you have three choices:
  1. Base your motion and animation on the frame rate and lock the frame rate to a constant value. You would do it something like this:
        int const LOCKED_FRAME_TIME = 50; // 50 ms is 20 fps    int old_time = some_function_to_get_the_time() - LOCKED_FRAME_TIME;    while ( game_is_running )    {        int current_time = some_function_to_get_the_time();        int delta_time = old_time - current_time;        if ( delta_time >= LOCKED_FRAME_TIME );        {            Update();            Render();            old_time = current_time - ( delta_time - LOCKED_FRAME_TIME );        }        else        {            Sleep( LOCKED_FRAME_TIME - delta_time );        }    } 
    This is the simplest method. I've never implemented a system like this, but from what I've heard, it is very difficult to get it to work well.

  2. Base your motion and animation off of real time and let the frame rate go as fast as possible. Something like this:
        int old_time = some_function_to_get_the_time();    while ( game_is_running )    {        int current_time = some_function_to_get_the_time();        int delta_time = old_time - current_time;        Update( delta_time );        Render();        old_time = current_time;    } 
    In most situations, this is the best choice.

  3. Base your motion and animation off the real time, update in fixed time steps, and let the frame rate go as fast as possible. Something like this:
        int UPDATE_TIME_STEP = 10; // 10 ms    int old_time = some_function_to_get_the_time();    while ( game_is_running )    {        int current_time = some_function_to_get_the_time();        int delta_time = old_time - current_time;        while ( delta_time >= UPDATE_TIME_STEP )        {            Update();            delta_time -= UPDATE_TIME_STEP;        }        Render();        old_time = current_time - delta_time;    } 
    For a game that uses serious physics, this is probably the best choice because it will give the most accurate results. The drawback over the previous choice is that the Update function is called several times per frame, slowing down the frame rate.
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
I have a tutorial on this.

*points to sig*

Its tutorial 10. I recommend you read tutorial 9 to understand how my timer class works since it's used in tutorial 10 to cap the frame rate.

Learn to make games with my SDL 2 Tutorials

Thanks a lot guys.
I'll have a look this afternoon, once I'll get home (i'm at work now :( ) and I'll let you know about my progess and if I didn't understand something.

Cheeers :)

This topic is closed to new replies.

Advertisement