Достижение постоянной частоты кадров в 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("%sn", 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("%sn", 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 ответов
Не спать.
вместо этого используйте функцию линейной интерполяции для вычисления вашей позиции с учетом текущего времени каждый раз через основной цикл. Это гарантирует, что независимо от оборудования космические корабли прибудут в пункты назначения одновременно (хотя на быстрой машине вы увидите больше шагов между ними).
также есть другие функции интерполяции / смешивания (например, линейная легкость, легкость, квадратичная легкость in/out, кубическая и т. д.) для других классные эффекты.
проверьте эту ссылку: Независимая Линейная Интерполяция Частоты Кадров
есть небольшой пример того, как это сделать в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;
}
Как сказал дикроче, не спи. Почему? Потому что большинство настольных операционных систем не являются жесткими системами реального времени, и поэтому sleep(10)
не значит "разбудите меня ровно через 10 миллисекунд", это значит "убедитесь, что я сплю не менее 10 миллисекунд". Это означает, что иногда вы проводите гораздо больше времени во сне, чем хотели. Это редко то, чего вы хотите. Если плавный геймплей важен, то обычно вы просто хотите визуализировать так часто, как можете - SDL_Flip обрабатывает это для вас, предоставляя ваши видеодрайверы не отключены VSync.
вместо сна дикроче предложил алгоритм линейной интерполяции для вычисления правильных положений для ваших объектов в любой момент времени. Хотя это разумная стратегия для многих игр, и я обычно использую ее сам, в некоторых случаях это может вызвать проблемы, если не обращаться осторожно:"Основы Интеграции" статьи объясняет это. Альтернатива, предложенная тем же автором следующая статья называется"Исправить Ваш Шаг Времени".