使用 c++ 模拟游戏循环中的时间

发布于 2024-08-13 00:26:51 字数 1195 浏览 5 评论 0原文

我正在 Linux 上使用 OpenGL 和 SDL 从头开始​​用 C++ 构建 3D 游戏,作为一种爱好,并了解有关该编程领域的更多信息。

想知道在游戏运行时模拟时间的最佳方法。显然我有一个看起来像这样的循环:

void main_loop()
{
    while(!quit)
    {
         handle_events();
         DrawScene();
         ...
         SDL_Delay(time_left());
    }
}

我使用 SDL_Delay 和 time_left() 来维持大约 33 fps 的帧速率。

我原以为我只需要一些全局变量,例如

int current_hour = 0;
int current_min = 0;
int num_days = 0;
Uint32 prev_ticks = 0;

然后一个函数,例如:

void handle_time()
{
    Uint32 current_ticks;
    Uint32 dticks;
    current_ticks = SDL_GetTicks();
    dticks = current_ticks - prev_ticks; // get difference since last time

    // if difference is greater than 30000 (half minute) increment game mins
    if(dticks >= 30000) {
         prev_ticks = current_ticks;
         current_mins++;
         if(current_mins >= 60) {
            current_mins = 0;
            current_hour++;
         }
         if(current_hour > 23) {
            current_hour = 0;
            num_days++;
         }
    }
 }

然后在主循环中调用handle_time()函数。

它编译并运行(目前使用 printf 将时间写入控制台),但我想知道这是否是最好的方法。有没有更简单的方法或者更有效的方法?

I am building a 3d game from scratch in C++ using OpenGL and SDL on linux as a hobby and to learn more about this area of programming.

Wondering about the best way to simulate time while the game is running. Obviously I have a loop that looks something like:

void main_loop()
{
    while(!quit)
    {
         handle_events();
         DrawScene();
         ...
         SDL_Delay(time_left());
    }
}

I am using the SDL_Delay and time_left() to maintain a framerate of about 33 fps.

I had thought that I just need a few global variables like

int current_hour = 0;
int current_min = 0;
int num_days = 0;
Uint32 prev_ticks = 0;

Then a function like :

void handle_time()
{
    Uint32 current_ticks;
    Uint32 dticks;
    current_ticks = SDL_GetTicks();
    dticks = current_ticks - prev_ticks; // get difference since last time

    // if difference is greater than 30000 (half minute) increment game mins
    if(dticks >= 30000) {
         prev_ticks = current_ticks;
         current_mins++;
         if(current_mins >= 60) {
            current_mins = 0;
            current_hour++;
         }
         if(current_hour > 23) {
            current_hour = 0;
            num_days++;
         }
    }
 }

and then call the handle_time() function in the main loop.

It compiles and runs (using printf to write the time to the console at the moment) but I am wondering if this is the best way to do it. Is there easier ways or more efficient ways?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

薄荷→糖丶微凉 2024-08-20 00:26:51

我之前在其他游戏相关主题中提到过这一点。 ,遵循 Glenn Fiedler 在他的游戏物理系列中的建议

与往常一样 想要做的是使用通过累积时间增量获得的恒定时间步。如果您想要每秒 33 次更新,那么您的恒定时间步长应该是 1/33。您也可以将其称为更新频率。您还应该将游戏逻辑与渲染分离,因为它们不属于在一起。您希望能够使用较低的更新频率,同时以机器允许的速度进行渲染。这是一些示例代码:

running = true;
unsigned int t_accum=0,lt=0,ct=0;
while(running){
    while(SDL_PollEvent(&event)){
        switch(event.type){
            ...
        }
    }
    ct = SDL_GetTicks();
    t_accum += ct - lt;
    lt = ct;
    while(t_accum >= timestep){
        t += timestep; /* this is our actual time, in milliseconds. */
        t_accum -= timestep;
        for(std::vector<Entity>::iterator en = entities.begin(); en != entities.end(); ++en){
            integrate(en, (float)t * 0.001f, timestep);
        }
    }
    /* This should really be in a separate thread, synchronized with a mutex */
    std::vector<Entity> tmpEntities(entities.size());
    for(int i=0; i<entities.size(); ++i){
        float alpha = (float)t_accum / (float)timestep;
        tmpEntities[i] = interpolateState(entities[i].lastState, alpha, entities[i].currentState, 1.0f - alpha);
    }
    Render(tmpEntities);
}

这可以处理欠采样和过采样。如果您像这里一样使用整数算术,那么无论机器有多慢或多快,您的游戏物理应该接近 100% 确定性。这就是以固定时间间隔增加时间的好处。用于渲染的状态是通过在先前状态和当前状态之间插值来计算的,其中时间累加器内的剩余值用作插值因子。这确保了无论时间步长有多大,渲染都是平滑的。

I've mentioned this before in other game related threads. As always, follow the suggestions by Glenn Fiedler in his Game Physics series

What you want to do is to use a constant timestep which you get by accumulating time deltas. If you want 33 updates per second, then your constant timestep should be 1/33. You could also call this the update frequency. You should also decouple the game logic from the rendering as they don't belong together. You want to be able to use a low update frequency while rendering as fast as the machine allows. Here is some sample code:

running = true;
unsigned int t_accum=0,lt=0,ct=0;
while(running){
    while(SDL_PollEvent(&event)){
        switch(event.type){
            ...
        }
    }
    ct = SDL_GetTicks();
    t_accum += ct - lt;
    lt = ct;
    while(t_accum >= timestep){
        t += timestep; /* this is our actual time, in milliseconds. */
        t_accum -= timestep;
        for(std::vector<Entity>::iterator en = entities.begin(); en != entities.end(); ++en){
            integrate(en, (float)t * 0.001f, timestep);
        }
    }
    /* This should really be in a separate thread, synchronized with a mutex */
    std::vector<Entity> tmpEntities(entities.size());
    for(int i=0; i<entities.size(); ++i){
        float alpha = (float)t_accum / (float)timestep;
        tmpEntities[i] = interpolateState(entities[i].lastState, alpha, entities[i].currentState, 1.0f - alpha);
    }
    Render(tmpEntities);
}

This handles undersampling as well as oversampling. If you use integer arithmetic like done here, your game physics should be close to 100% deterministic, no matter how slow or fast the machine is. This is the advantage of increasing the time in fixed time intervals. The state used for rendering is calculated by interpolating between the previous and current states, where the leftover value inside the time accumulator is used as the interpolation factor. This ensures that the rendering is is smooth, no matter how large the timestep is.

月下伊人醉 2024-08-20 00:26:51

除了已经指出的问题(您应该使用时间结构并将其传递给 handle_time() 并且您的分钟将每半分钟增加一次)之外,您的解决方案非常适合跟踪游戏中运行的时间。

然而,对于大多数需要经常发生的游戏事件,您可能应该将它们基于主游戏循环而不是实际时间,这样它们就会以相同的比例和不同的 fps 发生。

Other than the issues already pointed out (you should use a structure for the times and pass it to handle_time() and your minute will get incremented every half minute) your solution is fine for keeping track of time running in the game.

However, for most game events that need to happen every so often you should probably base them off of the main game loop instead of an actual time so they will happen in the same proportions with a different fps.

桜花祭 2024-08-20 00:26:51

您真正想读的格伦的帖子之一是修复您的时间步长! 。查找此链接后,我注意到 Mads 将您定向到他的 答案

One of Glenn's posts you will really want to read is Fix Your Timestep!. After looking up this link I noticed that Mads directed you to the same general place in his answer.

毁我热情 2024-08-20 00:26:51

我不是 Linux 开发人员,但您可能想看看使用计时器而不是轮询滴答声。

http://linux.die.net/man/2/timer_create

编辑:
SDL 似乎支持计时器:SDL_SetTimer

I am not a Linux developer, but you might want to have a look at using Timers instead of polling for the ticks.

http://linux.die.net/man/2/timer_create

EDIT:
SDL Seem to support Timers: SDL_SetTimer

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文