简体   繁体   English

SDL_Delay 与 SDL_GetTicks 混淆

[英]SDL_Delay messing with SDL_GetTicks

I am working on a simple rhythm game using SDL and I got stuck with keeping track of time(Ticks) in game.我正在使用 SDL 开发一个简单的节奏游戏,但我一直在跟踪游戏中的时间(滴答声)。 To make life simpler for myself I want to cap FPS at 60, but using SDL_Delay() affects SDL_GetTicks(), which is weird.为了让自己的生活更简单,我想将 FPS 限制在 60,但是使用 SDL_Delay() 会影响 SDL_GetTicks(),这很奇怪。 Shouldn't the SDL_GetTicks() function return the time from initialization (including time spent in SDL_Delay)? SDL_GetTicks() function 不应该返回初始化的时间(包括在 SDL_Delay 中花费的时间)吗? I have tried to do some stuff with std::chrono, but I can't wrap my head about counting time.我试图用 std::chrono 做一些事情,但我无法理解计算时间。 I had an idea to do something with threads, but I feel like there is a way simpler way.我有一个想法用线程做一些事情,但我觉得有一种更简单的方法。

Not sure if that's what you're looking for, but I think it's worth a try.不确定这是否是您要寻找的,但我认为值得一试。

Since there often isn't a 1:1 correspondence between the real time and the game time, it's necessary to implement a "mechanism" that takes into account the elapsed game time.由于实际时间和游戏时间之间通常不存在 1:1 的对应关系,因此有必要实现一个考虑游戏经过时间的“机制”。
The method I prefer to implement when dealing with framerate is one of the following:在处理帧率时我更喜欢实现的方法是以下之一:

  • capping the framerate to a fixed value (eg 60 FPS);将帧速率限制为固定值(例如 60 FPS);
  • using a delta time and updating the game logic according to that (as a function of delta time).使用增量时间并据此更新游戏逻辑(作为增量时间的 function)。

Capped "static" framerate限制“静态”帧率

Suppose you wanted the framerate to be capped to 60FPS.假设您希望将帧速率限制为 60FPS。 That means your game must update every 60th of 1000ms (1s).这意味着您的游戏必须每 1000 毫秒(1 秒)的 60 次更新一次。
To achieve this (and try to lock FPS at an approximately fixed value), you could test how long it took to render the previous frame and adjust the value passed to SDL_Delay() accordingly.为了实现这一点(并尝试将 FPS 锁定在一个近似固定的值),您可以测试渲染前一帧需要多长时间并相应地调整传递给SDL_Delay()的值。 Moreover, you could use an accumulator to get even closer to 60 FPS (rather than 62).此外,您可以使用累加器来获得更接近 60 FPS(而不是 62)的速度。

Below is an example written in C, which can be easily ported in C++.下面是一个用 C 编写的示例,可以很容易地移植到 C++ 中。

C Example: fixed 60 FPS cap C 示例:固定 60 FPS 上限

remainder is the accumulator remainder是累加器

static void capFrameRate(long *then, float *remainder)
{
    long wait, frameTime;

    wait = 16 + *remainder; // 16 is the integer part of 1000/60

    *remainder -= (int)*remainder;

    frameTime = SDL_GetTicks() - *then; // time it took the previous frame to render

    wait -= frameTime;

    if (wait < 1)
    {
        wait = 1;
    }

    SDL_Delay(wait);

    *remainder += 0.667; // 0.667 is the fractional part of 1000/60

    *then = SDL_GetTicks();
}

Main:主要的:

int main(int argc, char** argv)
{
    long then;
    float remainder;

    while (1)
    {
        // clear renderer
        
        // get input
        
        // update game logic

        // render frame
        
        capFramerate(&then, &remainder);
    }

    return 0;
}

Using delta time使用增量时间

You can keep track of the amount of elapsed game time since the last frame (that is the delta time ).您可以跟踪自上一帧以来经过的游戏时间(即delta time )。
For example, if you update an object position by some pixel per frame with object.x += 5;例如,如果您使用object.x += 5; , the object movement would depend on the current framerate (which can vary due to many often unpredictable factors); ,object 的运动将取决于当前的帧率(由于许多通常不可预测的因素,可能会有所不同); however, if we used a delta time, we could update the object position by some pixel per second : object.x += 5 * deltaTime;但是,如果我们使用增量时间,我们可以每秒更新 object position 一些像素: object.x += 5 * deltaTime; , and the object would always move by 5 pixels each second, even if the framerate rise to a extremely great value. ,而 object 将始终每秒移动 5 个像素,即使帧速率上升到一个非常大的值。

Frame limiting : using delta time, you don't need to delay the game anymore.帧限制:使用增量时间,您无需再延迟游戏。 However, allowing the game to run at whatever framerate the system allows might cause several issues.但是,允许游戏以系统允许的任何帧速率运行可能会导致几个问题。 The simplest solution to this, is to implement a frame limiting mechanism, which forces the game loop to wait until a target delta time has elapsed.最简单的解决方案是实现帧限制机制,强制游戏循环等待目标增量时间过去。
For example, if the target is 60 FPS, we want the target time to be 16.667 ms (1000/60) per frame.例如,如果目标是 60 FPS,我们希望目标时间为每帧 16.667 毫秒(1000/60)。 We then could use SDL_TICKS_PASSED() macro to achieve that: while (,SDL_TICKS_PASSED(SDL_GetTicks(); mTicksCount + 16));然后我们可以使用SDL_TICKS_PASSED()宏来实现: while (,SDL_TICKS_PASSED(SDL_GetTicks(); mTicksCount + 16)); . .

Last thing, it could be useful to watch out for delta time that's too high, since the game could pause (eg when debugging and stepping through a breakpoint).最后一件事,注意增量时间太长可能很有用,因为游戏可能会暂停(例如,在调试和单步执行断点时)。 To fix this problem, you can clamp the delta time to a maximum value (eg 0.05f), so the game simulation will never jump too far forward on any one frame.要解决此问题,您可以将增量时间限制为最大值(例如 0.05f),因此游戏模拟在任何一帧都不会向前跳得太远。

Here's an example which implements that.这是一个实现它的例子。

C++ Example: delta time + 60 FPS frame limiting + delta time upper bound C++ 示例:增量时间 + 60 FPS 帧限制 + 增量时间上限

Game class:游戏 class:

class Game
{
public:
    Game();
    void RunLoop();
private:
    // Stuff (window, renderer, ...)
    float deltaTime; // Delta time
    Uint32 mTicksCount; // Number of ticks since start of game

    // get input
    UpdateDeltaTime(); // Update delta time
    UpdateGameLogicExample(); // Update game objects as function of delta time
    // render frame
};

Method to update delta time:更新时间差的方法:

void Game::UpdateDeltaTime()
{
    // Frame limiting: wait until 16ms has elapsed since last frame
    while (!SDL_TICKS_PASSED(SDL_GetTicks(), mTicksCount + 16));

    // Delta time is the difference in ticks from last frame
    // (converted to seconds)
    deltaTime = (SDL_GetTicks() - mTicksCount) / 1000.0f;
    
    // Clamp maximum delta time value
    if (deltaTime > 0.05f)
    {
        deltaTime = 0.05f;
    }

    // Update tick counts (for next frame)
    mTicksCount = SDL_GetTicks();
}
void Game::UpdateGameLogicExample()
{
    gameObj.x += 5 * deltaTime; // Each second the game object will move by 5 pixel, independantly from the processor frequency or other external factors
}

Game loop:游戏循环:

void Game::RunLoop()
{
    while (true)
    {
        // clear renderer

        // get input

        UpdateDeltaTime();

        UpdateGameLogicExample();

        // render frame
    }
}

Some Useful References一些有用的参考资料

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM