Достижение постоянной частоты кадров в 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;
}

Ответ 1

Не спать.

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

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

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

Ответ 2

Существует небольшой пример того, как это сделать при 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

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

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