【问题标题】:FPS stutter - game running at double FPS specified?FPS 口吃 - 指定以双倍 FPS 运行的游戏?
【发布时间】:2013-09-03 13:23:37
【问题描述】:

我设法创建了一个允许游戏对象根据时间变化(而不是帧数变化)移动的系统。现在,为了实践,我正在尝试施加 FPS 限制器。

我遇到的问题是发生的游戏循环数量是我预期的两倍。例如,如果我尝试将帧数限制为 1 FPS,则 2 帧将在那一秒内通过 - 一个非常长的帧(~1 秒)和 1 个极短的帧(~15 毫秒)。这可以通过输出每个游戏循环之间的时间差(以毫秒为单位)来显示 - 示例输出可能是 993、17、993、16...

我尝试更改代码,以便在 FPS 受限之前计算增量时间,但无济于事。

定时器类:

#include "Timer.h"
#include <SDL.h>

Timer::Timer(void)
{
    _currentTicks = 0;
    _previousTicks = 0;
    _deltaTicks = 0;

    _numberOfLoops = 0;
}

void Timer::LoopStart()
{
    //Store the previous and current number of ticks and calculate the
    //difference. If this is the first loop, then no time has passed (thus
    //previous ticks == current ticks).
    if (_numberOfLoops == 0)
        _previousTicks = SDL_GetTicks();
    else
        _previousTicks = _currentTicks;

    _currentTicks = SDL_GetTicks();

    //Calculate the difference in time.
    _deltaTicks = _currentTicks - _previousTicks;

    //Increment the number of loops.
    _numberOfLoops++;
}

void Timer::LimitFPS(int targetFps)
{ 
    //If the framerate is too high, compute the amount of delay needed and
    //delay.
    if (_deltaTicks < (unsigned)(1000 / targetFps))
        SDL_Delay((unsigned)(1000 / targetFps) - _deltaTicks);
}

(部分)游戏循环:

//The timer object.
Timer* _timer = new Timer();

//Start the game loop and continue.
while (true)
{
    //Restart the timer.
    _timer->LoopStart();

    //Game logic omitted

    //Limit the frames per second.
    _timer->LimitFPS(1);
}

注意:我正在使用 SMA 计算 FPS;此代码已被省略。

【问题讨论】:

    标签: c++ visual-studio-2012 sdl frame-rate


    【解决方案1】:

    问题在于您的计时器结构是如何设置的。

    _deltaTicks = _currentTicks - _previousTicks;
    

    我查看了几次代码,根据您设置计时器的方式,这似乎是问题所在。

    在您的代码开始时,_currentTicks 将等于 0,_previousTicks 将等于 0,因此对于第一个循环,您的 _deltaTicks 将为 0。这意味着无论您的游戏逻辑如何,LimitFPS 都会出现异常。

    void Timer::LimitFPS(int targetFps)
    { 
        if (_deltaTicks < (unsigned)(1000 / targetFps))
            SDL_Delay((unsigned)(1000 / targetFps) - _deltaTicks);
    }
    

    我们刚刚计算出 _deltaTicks 在循环开始时为 0,因此无论您的游戏逻辑发生什么,我们都会等待整整 1000 毫秒。

    在下一帧中,_previousTicks 将等于 0,_currentTicks 现在将等于大约 1000 毫秒。这意味着 _deltaTicks 将等于 1000。

    我们再次点击了 LimitFPS 函数,但这次的 _deltaTicks 为 1000,因此我们等待 0 毫秒。

    这种行为会像这样不断翻转。

    wait 1000ms
    wait 0ms
    wait 1000ms
    wait 0ms
    ...
    

    那些等待 0ms 基本上是你的 FPS 的两倍。

    您需要在游戏逻辑的开始和结束时测试时间。

    //The timer object.
    Timer* _timer = new Timer();
    
    //Start the game loop and continue.
    while (true)
    {
        //Restart the timer.
        _timer->LoopStart();
    
        //Game logic omitted
    
        //Obtain the end time
        _timer->LoopEnd();
    
        //Now you can calculate the delta based on your start and end times.
        _timer->CaculateDelta();
    
        //Limit the frames per second.
        _timer->LimitFPS(1);
    }
    

    希望对您有所帮助。

    【讨论】:

    • 这也解释了您对 993、17、993、16 的计时...大数字是您要计算的实际 FPS,小数字是计算错误的增量。
    • 谢谢!我需要对对象的移动方式和 FPS 的计算方式进行一些更改,但这是一个很好的开始!
    • 别提了。我只是注意到它,因为我过去犯过类似的错误。您可以将LoopEndCaculateDelta 合并为一个函数,同时获取结束时间和计算增量。
    【解决方案2】:

    假设您的游戏循环仅在 SDL_Delay() 过期时触发,写起来还不够:

    void Timer::LimitFPS(int targetFps)
    {
        SDL_Delay((unsigned)(1000 / targetFps));
    }
    

    根据请求的目标 FPS 将游戏循环延迟适当的毫秒数?

    【讨论】:

    • 这不考虑游戏循环执行的时间。
    • 然后减去那个时间。不应该那么难。提示:延迟一秒后减去 _deltaTicks 是错误的。
    • 这次我已经减去了,所以我才知道你的错。
    • 不,你不是。您正在减去与渲染帧所需的时间不同的计时器延迟。这就是为什么你会得到奇怪的“长短长”帧序列。
    猜你喜欢
    • 2014-11-08
    • 2012-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多