【问题标题】:C++: Counting total frames in a gameC ++:计算游戏中的总帧数
【发布时间】:2011-06-08 19:23:39
【问题描述】:

不是一个很好的标题,但我不知道该取什么名字。

无论如何,我正在用long int 计算我的游戏中的总帧数(这样我就可以计算平均 FPS)。万一游戏持续很长时间,我应该怎么做才能确保我的long int 不会超过其限制?如果它确实超过了它的极限会发生什么?

谢谢。

【问题讨论】:

  • 为什么需要整个游戏的平均值?为什么不只保持最后一秒/最后几秒的计数值,然后不再打扰?
  • gamedev.stackexchange.com 询问您是否有更多的运气?这不是修辞。老实说,我想知道。
  • @Jake 这个问题其实和游戏没什么关系,但是我不知道怎么命名,问这个问题。
  • 我猜会发生什么在某种程度上取决于您的编译器和系统。我建议将数字设置为最大值,然后增加以查看会发生什么。
  • @Jay:我不这么认为。要溢出它,您需要以每秒 30 帧的速度播放 1169884834710 年。

标签: c++ memory-management int long-integer increment


【解决方案1】:

任何类型的计数器都存在此问题。

对于您的具体问题,我不会担心。

在最坏的情况下(在 32 位计算机/控制台上),long int 的计数高达 20 亿(甚至更多)。假设您的游戏每秒运行 1000 帧(这很多!),溢出计数器需要 20000000 秒:超过 5000 小时,超过 231 天。

我敢肯定,如果您尝试运行这么长时间,其他事情会导致您的游戏停止!

【讨论】:

  • @peoro 是的,我自己做了一些计算,但这是以防万一。当计数器过度增加时会发生什么?
  • @Jay:如果它被声明为无符号,它会回到0。如果不是,它会变成一个负数。具体效果取决于您的代码。
  • @Jay:当整数溢出时(即:它超过了它可以包含的最大值)它从它可以包含的最小值重新开始。会发生什么取决于您对整数执行的操作,您的 FPS 指示器很可能会显示您的游戏以 -X 帧/秒的速度运行。
  • @luiscubal @peoro 有符号溢出会导致 C 和 C++ 中的未定义行为。不能保证回绕到最负数。然而,无符号溢出是明确定义的。见this Question
【解决方案2】:

我会考虑使用 exponentially-weighted moving average。这种方法将一石二鸟:它会避免积累大量的问题,并且它还将适应最近的行为,以便在 2010 年累计平均 100fps 不会扭曲平均值,从而使 2fps 速率在 2011 年一个月左右似乎可以接受:)。

【讨论】:

    【解决方案3】:

    整个游戏时长的平均 FPS 似乎不是一个非常有用的统计数据。通常,您会希望测量峰值和谷值,例如最高 fps/最低 fps 以及低于和高于阈值的帧数。

    但实际上,我不会担心。即使您只使用 32 位无符号整数,您的游戏也可以在 19884 小时内以 60fps 的速度运行,然后才会溢出。你应该没事。

    编辑:

    在这种情况下检测溢出的最佳方法是检查整数在增加后是否减小了值。如果是这样,您可以只保留另一个计数器,该计数器是您溢出的次数。

    【讨论】:

      【解决方案4】:

      您可以主动检查算术运算中的溢出。例如。 SafeInt 可以为您做到这一点。当然,性能比i++ 差。

      但是,如果总是加一,32 位整数不太可能溢出。

      【讨论】:

      • 对于这种特定情况,我不建议这样做。避免溢出的其他解决方案可能是使用更大的类型来存储值:long long,甚至是一些高级数学库提供的动态长度整数。但对于这种情况,这不值得,它只会让事情变得更慢、更复杂,而且总的来说更糟而没有任何收获。
      • 我同意 - 因此我说性能会下降。也许我应该添加它可能会显着降低,因为使用 SafeInt 将需要一个 try-catch-block。但问题是如何检测溢出。对于具体情况,我想我们都同意这个问题不太可能发生,你应该忽略它。
      【解决方案5】:

      如果 long int 为 32 位,最大值为 2^31-1,因此 1ms 更新将在 24.9 天后溢出,而不是 231 [2^31/1000/60/60/24]。

      希望不要太OT...通常对于游戏来说,这可能不是真正的问题,但对于其他应用程序来说却是这样。一个需要小心的常见错误是做类似的事情

      extern volatile uint32_t counter;
      uint32_t one_second_elapsed = counter + 1000;
      while ( counter < one_second_elapsed ) do_something();
      

      如果 counter + 1000 溢出,则不会调用 do_something()。检查的方法是,

      uint32_t start = counter;
      while ( counter - start < 1000 ) do_something();
      

      【讨论】:

        【解决方案6】:

        最好使用少量帧的平均值。您提到要计算平均值,但实际上没有理由保留如此大量的样本来计算平均值。只需在一小段时间内保持运行总帧时间(其中小的可能在 10-50 帧之间 - 我们通常使用 16)。然后,您可以使用该总数来计算平均每秒帧数。此方法还有助于平滑帧时间报告,以免数字到处乱跳。不过要注意的一件事是,如果您在太长的时间段内进行平均,帧速率峰值会变得更加“隐藏”,这意味着如果这些帧仅经常发生,则可能更难发现导致帧速率下降的帧.

        我认为这样的事情就足够了(遵循未经测试的代码):

        // setup some variables once
        const int Max_samples = 16;    // keep at most 16 frametime samples
        int FPS_Samples = 0;
        int Current_sample = 0;
        int Total_frametime = 0.0f;
        float Frametimes[Max_samples];
        for ( int i = 0; i < Max_samples; i++ ) {
            Frametimes[i] = 0.0f;
        

        然后当你计算你的帧时间时,你可以这样做:

        // current_frametime is the new frame time for this frame
        Total_frametime -= Frametimes[Current_sample];
        Total_frametime += current_frametime;
        Frametimes[Current_sample] = current_frametime;
        Current_sample = ( Current_sample + 1 ) % Max_samples;   // move to next element in array
        
        Frames_per_second = Max_samples / Total_frametime;
        

        这是一个粗略的剪辑,可能会使用一些错误检查,但它给出了大致的想法。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-04-10
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多