2010-03-30 92 views
11

我試圖製作一個以恆定幀速率運行的SDL程序。但是我發現即使我的程序滯後很多,並且跳過了很多幀(儘管它運行在低幀並且沒有渲染太多)。在SDL中實現恆定幀速率

你們有什麼建議讓我的程序運行更流暢嗎?

#include "SDL.h" 
#include "SDL/SDL_ttf.h" 

//in milliseconds 
const int FPS = 24; 
const int SCREENW = 400; 
const int SCREENH = 300; 
const int BPP = 32; 

void apply_surface(int x, int y, SDL_Surface* source, SDL_Surface* destination) { 

    SDL_Rect offset; 

    offset.x = x; 

    offset.y = y; 



    if(SDL_BlitSurface(source, NULL, destination, &offset) < 0) { 
     printf("%s\n", SDL_GetError()); 
    } 

} 

int main(int argc, char* argv[]) { 
    //calculate the period 
    double period = 1.0/(double)FPS; 
    period = period * 1000; 
    int milliPeriod = (int)period; 
    int sleep; 

    SDL_Init(SDL_INIT_EVERYTHING); 
    TTF_Init(); 

    TTF_Font* font = TTF_OpenFont("/usr/share/fonts/truetype/freefont/FreeMono.ttf", 24); 
    SDL_Color textColor = { 0x00, 0x00, 0x00 }; 

    SDL_Surface* screen = SDL_SetVideoMode(SCREENW, SCREENH, BPP, SDL_SWSURFACE); 
    SDL_Surface* message = NULL; 

    Uint32 white = SDL_MapRGB(screen->format, 0xFF, 0xFF, 0xFF); 

    SDL_Event event; 

    char str[15]; 

    Uint32 lastTick; 
    Uint32 currentTick; 
    while(1) { 
     while(SDL_PollEvent(&event)) { 
      if(event.type == SDL_QUIT) { 
       return 0; 
      } 
      else { 
       lastTick = SDL_GetTicks(); 

       sprintf(str, "%d", lastTick); 
       message = TTF_RenderText_Solid(font, str, textColor); 
       if(message == NULL) { printf("%s\n", SDL_GetError()); return 1; } 

       //the actual blitting 
       SDL_FillRect(screen, &screen->clip_rect, white); 
       apply_surface(SCREENW/2, SCREENH/2, message, screen); 

       currentTick = SDL_GetTicks(); 

       //wait the appropriate amount of time 
       sleep = milliPeriod - (currentTick - lastTick); 
       if(sleep < 0) { sleep = 0; } 
       SDL_Delay(sleep); 

       SDL_Flip(screen); 
      } 
     } 
    } 

    TTF_CloseFont(font); 
    TTF_Quit(); 
    SDL_Quit(); 

    return 0; 
} 

回答

6

不要睡覺。

取而代之的是,使用線性插值函數來計算每次通過主循環時的當前時間。這樣做可以保證無論硬件如何,宇宙飛船同時到達目的地(儘管在快速的機器上,你會看到更多的步驟)。另外還有其他的插值/混合功能(比如線性緩和,緩和,二次緩進/立體,等等)對於其他很酷的效果。

檢查此鏈接了: Frame Rate Independent Linear Interpolation

+16

我認爲睡覺以節省電池可能是個好主意。插值是好的,在某些遊戲中無論如何都是好的,但如果我的遊戲在使用時達到60FPS,比如20%的CPU,絕對不需要更快地運行,因爲顯示器最有可能運行在60Hz無論如何,所以我只是在筆記本電腦和設備上浪費電池壽命。 – 2011-09-09 11:54:57

+0

@TomA SDL不會執行VSync來限制幀率嗎? – 2014-01-11 03:18:28

+0

您可以在SDL 2.x中選擇3種類型的V-Sync。參見SDL_GL_SetSwapInterval() – Michaelangel007 2014-03-14 19:47:50

4

有是如何做到這一點的http://www.libsdl.org/release/SDL-1.2.15/docs/html/guidetimeexamples.html一個小例子:

#define TICK_INTERVAL 30 

static Uint32 next_time; 

Uint32 time_left(void) 
{ 
    Uint32 now; 

    now = SDL_GetTicks(); 
    if(next_time <= now) 
     return 0; 
    else 
     return next_time - now; 
} 


/* main game loop */ 

    next_time = SDL_GetTicks() + TICK_INTERVAL; 
    while (game_running) { 
     update_game_state(); 
     SDL_Delay(time_left()); 
     next_time += TICK_INTERVAL; 
    } 
+3

鏈接已死亡 – user1040495 2014-02-15 20:18:18

+0

@Jjpx:不再了,但我想這是一個鏈接專用的答案,所以我想我會把它粘貼在這裏...... – SamB 2014-11-27 22:05:37

3

正如dicroce說,不睡覺。爲什麼?由於大多數桌面操作系統不是硬實時系統,因此sleep(10)並不意味着「準確地在10毫秒內喚醒我」,這意味着「確保我睡着了至少10毫秒」。這意味着有時你會花費比想要的更長的時間。這很少是你想要的。如果流暢的遊戲玩法很重要,那麼一般情況下,您只需要儘可能頻繁地進行渲染 - SDL_Flip可爲您提供處理,只要您的視頻驅動程序不會禁用VSync。

除了睡覺,dicroce建議使用線性插值算法來計算任何給定時間實體的正確位置。雖然這對許多遊戲來說都是合理的策略,而且我經常使用自己的遊戲,但如果不謹慎處理,它可能會在某些情況下導致問題:「Integration Basics」文章解釋了其中的一些情況。在同一作者的下一篇文章中提供的替代方案,名爲「Fix Your Timestep」。