[Day13] 时间处理

今日目标

  • 限制帧数
  • Framerate Independent

设计错误

这个是我再次参考Game Progarmming Pattern - Game Loop这一章节,与Ralylib这个纯C的libary得到的一个结果。

之前想说设计是这样子:

while (true) {
StartScene();

// Input here
// Update here

BeginRendering();

// Render here

EndRendering();
EndScene();
}

然後把时间的处理放在StartScene()EndScene(),但看了Game Programming Pattern之後发现,正常来说如果要限制FPS,达到Framerate Indepent的话,应该是只要对Update(),也就是逻辑的地方做限制,但上面这个的设计是连Input()Render()都在里面了。後来在github上找到一个游戏libary - Raylib(没错!是当"抄"人的时候了),他的做法是这样

while (true) {

// Input here
// Update here

BeginDrawing();

// Render here

EndDrawing();

Raylib是把delta time称作frame time,是由update time跟render time合并起来的。

BeginDrawing()呼叫时会计算update time然後储存;EndDrawing()呼叫时,同理会更新render time。然後这两个相加,就是这一个frame的处理时间,再来就是一个重点了。

// in `BeginDrawing`
WINDOW.Time.current_time = (float)glfwGetTime();
WINDOW.Time.update_time = WINDOW.Time.current_time - WINDOW.Time.previous_time;
WINDOW.Time.previous_time = WINDOW.Time.current_time;
// in `EndDrawing`
WINDOW.Time.current_time = (float)glfwGetTime();
WINDOW.Time.render_time = WINDOW.Time.current_time - WINDOW.Time.previous_time;
WINDOW.Time.previous_time = WINDOW.Time.current_time;

WINDOW.Time.frame_time = WINDOW.Time.render_time + WINDOW.Time.update_time; // 这个就是delta time

如果要限制60 FPS,也就是约0.01666秒的时间,update time加上render time的时间可能会小於0.016666秒,於是可以用一些方法让game loop强制处理剩余的秒数。

第一种: Sleep

没错!让执行序强制停止N秒,没记错的话,SDL中的SDL_Delay底下就是调用各名台相关的Sleep

第二种 while loop

我是用这一种方式,在EndDrawing()那边接下去写来着:

    if (WINDOW.Time.frame_time < WINDOW.Time.target_time) {
        float wait_time = WINDOW.Time.target_time - WINDOW.Time.frame_time;

        // wait time
        float start_wait_time = glfwGetTime();
        float end_wait_time = 0.0f;
        while ((end_wait_time - start_wait_time) < wait_time) {
            end_wait_time = glfwGetTime();
        }
        
        // add extra time
        WINDOW.Time.current_time = (float)glfwGetTime();
        float extra_time = WINDOW.Time.current_time - WINDOW.Time.previous_time;
        WINDOW.Time.previous_time = WINDOW.Time.current_time;

        WINDOW.Time.frame_time += extra_time;
    }

实验: Framerate Independent

昨天有提到Framerate Independent,来实验一下

在不同的FPS下,高FPS会有较低的delta time;低FPS的会有较高的delta time。故两者最後都会在同时间抵达目的地。

这边贴上,我简单测的code

    SetTargetFPS(10); // change fps here!!
    
    float current_time = GetTime();
    float previous_time = GetTime();
    float time_elapsed = 0.0f;

    int test_count = 0;

    // Game loop
    while (IsWindowRunning()) {
        
        if (e.pos.x > 800.0f) {
            current_time = GetTime();
            time_elapsed = current_time - previous_time;
            previous_time = current_time;

            LogInfo("Time elapsed: %f", time_elapsed);
            e.pos.x = 0.0f;
        }

        e.pos = V2fAdd(e.pos, V2fScalef(e.speed, GetDeltaTime()));

        StartScene(COLOR_BLACK);
        BeginRendering();

        DrawRectangle(e.pos, size, 0.0f, e.c);

        EndRendering();
        EndScene();
    }

有了delta time的调节,不管是10 FPS还是144 FPS都会在同个(会有0.0几的误差...)时间,从左走到右。如果没有个话,就会是全看CPU的频率在表现跟处理了。

参考

备注: raylib作者还有在更新,我贴的是在00a763e的这个版本

完成!这是今日的成果


<<:  一次一件事就好,对你而言最重要的东西是什麽?

>>:  IOS、Python自学心得30天 Day-20 .h5 to .tflite to .mlmodel

[从0到1] C#小乳牛 练成基础程序逻辑 Day 3 - Hello World

打Code打Call | 主控台App | Hello World 🐄点此填写今日份随堂测验 ...

数位签章(digital signature)

-数位签章 使用您的私钥加密代码的指纹或对代码进行散列并使用您的私钥加密结果是生成数字签名的 改写...

How to set video ringtone in mobile?

Today mobile technology has become so advanced tha...

Day 15 实作调色盘App(3/3)

今天我们把这个App完成~ 首先,先到我们的Main.storyboard 点击右上角 + ->...

Day 30 - Finite State Machine x XState 推荐学习资源

说到学习资源,最容易学习的方式就是从母语开始 中文文章 Jerry Hong 的部落格 Jerry ...