如何为游戏循环制作计时器?

4

我想计算帧率,并将其限制为60,但是我通过谷歌查找了一些代码,完全看不懂。


兴趣所在,假设帧率限制为60,在75Hz屏幕上会是什么样子? - stijn
你可能会想在DirectX中开启垂直同步,这将限制渲染到屏幕刷新率的倍数。通常只需获取上一帧以来的时间并按照该时间增加游戏状态即可(独立于每秒帧数的世界)。 - ssube
5个回答

3

Delta time是最终时间减去原始时间的差值。

dt= t-t0

这个增量时间只是速度变化时经过的时间。
函数的导数表示函数相对于其变量之一的无穷小变化。函数相对于变量的导数定义为:
                f(x + h) - f(x)
f'(x) = lim    -----------------
        h->0           h

http://mathworld.wolfram.com/Derivative.html

#include<time.h>
#include<stdlib.h>
#include<stdio.h>
#include<windows.h>
#pragma comment(lib,"winmm.lib")

void gotoxy(int x, int y);
void StepSimulation(float dt);

int main(){

  int NewTime = 0;
  int OldTime = 0;
  float dt = 0;
  float TotalTime = 0;
  int FrameCounter = 0;
  int RENDER_FRAME_COUNT = 60;

  while(true){

        NewTime = timeGetTime();    
        dt = (float) (NewTime - OldTime)/1000; //delta time
        OldTime = NewTime;

        if (dt > (0.016f)) dt = (0.016f);  //delta time
        if (dt < 0.001f) dt = 0.001f;

        TotalTime += dt;

        if(TotalTime > 1.1f){ 
            TotalTime=0;
            StepSimulation(dt);
            }

        if(FrameCounter >= RENDER_FRAME_COUNT){           
            // draw stuff
            //Render(); 

            gotoxy(1,2);
            printf(" \n");
            printf("OldTime      = %d \n",OldTime);
            printf("NewTime      = %d \n",NewTime);
            printf("dt           = %f  \n",dt);
            printf("TotalTime    = %f  \n",TotalTime);
            printf("FrameCounter = %d fps\n",FrameCounter);
            printf("   \n");
            FrameCounter = 0;

        } 
        else{
            gotoxy(22,7);
            printf("%d  ",FrameCounter);
            FrameCounter++;

        }


    }

    return 0;
}

void gotoxy(int x, int y){
    COORD coord;
    coord.X = x; coord.Y = y;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
    return;
}

void StepSimulation(float dt){
    // calculate stuff
   //vVelocity += Ae * dt;

}

3
如果你想要60帧率,你需要计算每一帧所需的时间。在这种情况下,是16.67毫秒。所以你需要一个循环,在每16.67毫秒时完成一次。
通常它的执行过程是(简单来说):获取输入、进行物理操作、渲染,暂停直至经过16.67毫秒。
通常是通过在循环顶部保存时间,然后在最后计算差异并休眠或循环无偿地等待该持续时间来完成。 这篇文章 描述了几种不同的游戏循环方式,包括你想要的那种,尽管我会在本文中使用更高级的替代方案之一。

我知道这很老了...但链接已经失效了。 - Bhriguraj Salponia

2

您不应该尝试限制fps。唯一的原因是,如果您没有使用delta时间并且希望每个帧具有相同的长度。即使是最简单的游戏也无法保证这一点。

但是,您可以将delta时间切成固定大小,然后保留剩余部分。

这是我最近编写的一些代码。它没有经过彻底测试。

void GameLoop::Run()
{
    m_Timer.Reset();

    while(!m_Finished())
    {
        Time delta = m_Timer.GetDelta();
        Time frameTime(0);
        unsigned int loopCount = 0;

        while (delta > m_TickTime && loopCount < m_MaxLoops)
        {
            m_SingTick();
            delta -= m_TickTime;
            frameTime += m_TickTime;
            ++loopCount;
        }
        m_Independent(frameTime);

        // add an exception flag later.
        // This is if the game hangs
        if(loopCount >= m_MaxLoops)
        {
            delta %= m_TickTime;
        }

        m_Render(delta);
        m_Timer.Unused(delta);
    }
}

会员对象是Boost插槽,因此不同的代码可以使用不同的时间方法进行注册。独立插槽用于诸如键映射或更改音乐等不需要那么精确的事物。SingTick适用于物理学,在这里,如果您知道每一个tick都是相同的,那么它会更容易,但您不希望穿过墙壁。Render使用delta,因此动画运行流畅,但必须记住在下一个SingTick上进行核算。希望这能帮到你。

1

有很多好的理由,为什么你不应该以这种方式限制你的帧率。其中一个原因是,正如Stijn所指出的那样,不是每个显示器都能以恰好60fps运行,另一个原因是计时器的分辨率不足,还有一个原因是即使给定足够的分辨率,两个独立的计时器(监视器刷新和你的)并行运行将始终因随机不准确性而失去同步时间(它们必须!),最重要的原因是根本没有必要。

请注意,在Windows下,默认计时器分辨率为15ms,您可以通过使用timeBeginPeriod获得的最佳分辨率为1ms。因此,您最多可以等待16ms或17ms。在60fps下的一个帧是16.6666ms,你怎么等待16.6666ms呢?

如果你想将游戏速度限制在显示器的刷新率上,请启用垂直同步。这将精确地做到你想要的,而且没有同步问题。垂直同步也有其特殊性(例如当一帧需要16.67ms时,你会得到有趣的惊喜),但它是目前最好的解决方案。

如果你想这么做的原因是为了将你的模拟适应渲染循环,那么this对你来说是必读的。

-3

看看这个:

//Creating Digital Watch in C++
#include<iostream>
#include<Windows.h>
using namespace std;

struct time{

int hr,min,sec;
};
int main()
{
time a;
a.hr = 0;
a.min = 0;
a.sec = 0;

for(int i = 0; i<24; i++)
{
    if(a.hr == 23)
    {
        a.hr = 0;
    }

    for(int j = 0; j<60; j++)
    {
        if(a.min == 59)
        {
            a.min = 0;
        }

        for(int k = 0; k<60; k++)
        {
            if(a.sec == 59)
            {
                a.sec = 0;
            }

            cout<<a.hr<<" : "<<a.min<<" : "<<a.sec<<endl;
            a.sec++;
            Sleep(1000);
            system("Cls");
        }
    a.min++;

}

    a.hr++;
}

}

看起来这是一个将精确运行24小时并在每秒打印小时:分钟:秒的程序。这对于游戏循环有什么好处吗? - mchiasson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接