使用SDL2实现稳定的60fps

问题描述 投票:0回答:1

我一直在努力寻找有关如何在使用 SDL2 的游戏中实现稳定 60 fps 的行业标准的信息,在阅读了一堆文章后,我迭代了不同的想法,直到能够测量 60 (+- 0.1 ) 我的游戏中的 fps。

下面是我目前的方法,我正在寻找验证它是否完全错误/纠正实际游戏(2D)如何做到这一点。

const float TARGET_FPS = 60.04f;
const float SCREEN_TICKS_PER_FRAME = 1000.0f / static_cast<float>(TARGET_FPS);
...
class Timer{
public:
    Uint64 started_ticks = 0;

    Timer()
            :started_ticks{SDL_GetPerformanceCounter()}
    {}

    void restart() {
        started_ticks = SDL_GetPerformanceCounter();
    }

    float get_time() {
        return (static_cast<float>(SDL_GetPerformanceCounter() - started_ticks) / static_cast<float>(SDL_GetPerformanceFrequency()) * 1000.0f);
    }
};

...

float extra_time{0};
Timer fps_cap_timer{};

while(game_loop_condition){
    fps_cap_timer.restart();
    ...
    render();
    while((fps_cap_timer.get_time() + extra_time) < SCREEN_TICKS_PER_FRAME) {
            SDL_Delay(1); // I am aware of the issues with delay on different platforms / OS scheduling
        }
    if (fps_cap_timer.get_time() < (SCREEN_TICKS_PER_FRAME)) {
            extra_time -= SCREEN_TICKS_PER_FRAME - fps_cap_timer.get_time();
        }
        else {
            extra_time += fps_cap_timer.get_time() - SCREEN_TICKS_PER_FRAME;
        }
}

采用这种方法,总会有一点额外的时间在 0 - 1ms 之间波动,这会导致 FPS 略有下降 <60, therefore I adjusted the target FPS to 60.04 and I am getting 60±0.1 FPS.

那么..这实际上可行吗,还是我搞砸了一些事情,应该更多地阅读该主题?

c++ sdl game-development sdl-2
1个回答
0
投票

注意:通常并不依赖 FPS。相反,测量自最后一帧以来的时间,并将其用作游戏对象移动距离的乘数。

在某些情况下可能需要相当精确的 FPS,但由于您在循环的每次迭代中重新启动计时器,因此它会发生漂移。我建议只向计时器添加一个固定的持续时间,然后等到该时间点发生。仅使用标准库类和函数,它可能看起来像这样:

#include <chrono>
#include <cstdint>
#include <iostream>
#include <thread>
#include <type_traits>

template<std::intmax_t FPS>
class Timer {
public:
    // initialize Timer with the current time point:
    Timer() : tp{std::chrono::steady_clock::now()} {}

    // a fixed duration with a length of 1/FPS seconds
    static constexpr std::chrono::duration<double, std::ratio<1, FPS>>
        time_between_frames{1};

    void sleep() {
        // add to the stored time point
        tp += time_between_frames;

        // and sleep almost until the new time point
        std::this_thread::sleep_until(tp - std::chrono::microseconds(100));

        // less than 100 microseconds busy wait
        while(std::chrono::steady_clock::now() < tp) {}
    }

private:
    // the time point we'll add to in every loop
    std::chrono::time_point<std::chrono::steady_clock,
                            std::remove_const_t<decltype(time_between_frames)>>
        tp;
};

繁忙的等待是为了使 FPS 尽可能准确,但它会消耗 CPU,因此您希望保持它尽可能短。 100μs 可能太多了,所以如果您发现精度足够好,您可以将其缩短或什至将其删除。

那么你的游戏循环将是:

int main() {
    // setup ...

    Timer<60> fps_cap_timer; // 60 FPS
    while(true) {
        render();
        fps_cap_timer.sleep(); // let it sleep any time remaining
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.