Достижение постоянной частоты кадров в SDL - PullRequest
12 голосов
/ 31 марта 2010

Я пытаюсь создать программу 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;
}

Ответы [ 3 ]

7 голосов
/ 31 марта 2010

Вот небольшой пример того, как это сделать на 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;
    }
6 голосов
/ 31 марта 2010

Не спи.

Вместо этого используйте функцию линейной интерполяции для вычисления вашей позиции, учитывая текущее время каждый раз в основном цикле. Это гарантирует, что, независимо от аппаратного обеспечения, космические корабли прибудут в пункт назначения одновременно (хотя на быстрой машине вы увидите больше промежуточных шагов).

Также есть другие функции интерполяции / смешивания (например, линейное замедление, замедление, квадратичное облегчение в / из, кубическое и т.д.) для других интересных эффектов.

Проверьте эту ссылку: Независимая линейная интерполяция частоты кадров

3 голосов
/ 31 марта 2010

Как сказал Дикроче, не спи. Зачем? Поскольку большинство настольных операционных систем не являются жесткими системами реального времени, и поэтому sleep(10) не означает «разбудите меня ровно через 10 миллисекунд» , это означает «убедитесь, что я сплю хотя бы 10 миллисекунд ". Это означает, что иногда вы спите намного дольше, чем хотели. Это редко то, что вы хотите. Если важен плавный игровой процесс, то, как правило, вы просто хотите рендерить как можно чаще - SDL_Flip справится с этим, если у ваших видеодрайверов не отключен VSync.

Вместо сна dicroce предложил алгоритм линейной интерполяции, чтобы рассчитать правильные позиции для ваших сущностей в любой момент времени. Хотя это разумная стратегия для многих игр, и я обычно использую ее самостоятельно, в некоторых случаях она может вызвать проблемы, если ее не обрабатывать осторожно: статья " Основы интеграции " объясняет некоторые из них. Альтернатива, предложенная в следующей статье того же автора, называется " Fix Your Timestep ".

...