问题 在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;
}

1244
2018-03-30 20:38


起源



答案:


别睡觉

相反,使用线性插值函数计算您每次通过主循环时的当前时间的位置。这样做可以保证无论硬件是什么,太空船都会同时到达目的地(虽然在快速机器上,你会看到更多的步骤)。

对于其他很酷的效果,还有其他插值/混合功能(如线性缓入,缓出,二次易进/出,立方等)。

检查此链接: 帧速率无关线性插值


6
2018-03-30 20:48



我认为睡觉节省电池可能是个好主意。插值很好,在某些游戏中无论如何都可以做到这一点,但是如果我的游戏在使用时可以达到60FPS,比如20%的CPU,那么绝对不需要运行得更快,因为显示器最有可能以60Hz运行无论如何,所以我只是在笔记本电脑和设备上浪费电池寿命。 - Tomas Andrle
@TomA SDL不执行VSync,无论如何限制帧速率吗? - Jonathan Baldwin
您可以选择3种类型的V-Sync与SDL 2.x.请参阅SDL_GL_SetSwapInterval() - Michaelangel007
“这样做可以保证无论硬件,太空船同时到达目的地” - 如果浮点计算具有无限分辨率,情况就是如此。事实是,在不同的帧速率下,事物会稍微移动(或者不是那么轻微 - 取决于)不同的速度。在浮点数学中, 0.1 * 10 != 10。这种技术可能很有用,但要小心处理。 - jacwah


答案:


别睡觉

相反,使用线性插值函数计算您每次通过主循环时的当前时间的位置。这样做可以保证无论硬件是什么,太空船都会同时到达目的地(虽然在快速机器上,你会看到更多的步骤)。

对于其他很酷的效果,还有其他插值/混合功能(如线性缓入,缓出,二次易进/出,立方等)。

检查此链接: 帧速率无关线性插值


6
2018-03-30 20:48



我认为睡觉节省电池可能是个好主意。插值很好,在某些游戏中无论如何都可以做到这一点,但是如果我的游戏在使用时可以达到60FPS,比如20%的CPU,那么绝对不需要运行得更快,因为显示器最有可能以60Hz运行无论如何,所以我只是在笔记本电脑和设备上浪费电池寿命。 - Tomas Andrle
@TomA SDL不执行VSync,无论如何限制帧速率吗? - Jonathan Baldwin
您可以选择3种类型的V-Sync与SDL 2.x.请参阅SDL_GL_SetSwapInterval() - Michaelangel007
“这样做可以保证无论硬件,太空船同时到达目的地” - 如果浮点计算具有无限分辨率,情况就是如此。事实是,在不同的帧速率下,事物会稍微移动(或者不是那么轻微 - 取决于)不同的速度。在浮点数学中, 0.1 * 10 != 10。这种技术可能很有用,但要小心处理。 - jacwah


有一个小例子,说明如何做到这一点 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;
    }

5
2018-03-30 21:08



这个链接已经死了 - user1040495
@Jjpx:不再了,但我猜这只是一个链接答案,所以我想我会把它贴在这里...... - SamB


正如dicroce所说,不要睡觉。为什么?因为大多数桌面操作系统不是硬实时系统,因此 sleep(10) 并不意味着 “准确地叫醒我10毫秒”, 它的意思是 “确保我睡了至少10毫秒”。这意味着有时候你的睡眠时间比你想要的长很多。这很少是你想要的。如果流畅的游戏玩法很重要,那么通常你只想尽可能频繁地渲染 - SDL_Flip为你处理,提供你的视频驱动程序没有禁用VSync。

而不是睡觉,dicroce建议使用线性插值算法来计算任何给定时间的实体的正确位置。虽然对于许多游戏来说这是一个合理的策略,而且我经常使用自己的游戏,但如果不仔细处理,它会在某些情况下引起问题:“集成基础知识“文章解释了其中的一些内容。同一作者的下一篇文章提供了另一种选择,称为”修复你的时间步”。


3
2018-03-31 10:13