2016-11-28 115 views
0

我目前正在做一个2D游戏引擎使用C#,GDI和已经建立了简单的框架帽。游戏只能渲染60fps。C#GDI游戏循环(FPS计数器)

据我知道有一个与代码没有问题,但是,我只想呈现60fps的更清洁的方式并没有更多的。

这里是我的代码,任何帮助将是巨大的

 public void Run() 
    { 
     window.Show(); 
     window.Focus(); 

     Initialize(); 

     isRunning = true; 
     canRender = true; 

     timer = new Stopwatch(); 
     timer.Start(); 

     // the amount of milliseconds needed to pass before rendering next frame 
     double frameCapCounter = 16.666666; 

     while (isRunning) 
     { 
      Application.DoEvents(); 

      if (window.Focused) 
      { 
       if (timer.ElapsedMilliseconds >= frameCapCounter) 
       { 
        canRender = true; 
        frames += 1; // update amount of frames 
        frameCapCounter += 16.666666; // increment counter 
       } 
       else 
       { 
        canRender = false; 
       } 

       // this is used to check if a second has passed, and if so 
       // we set the fps variable to the amount of frames rendered 
       // and reset all variables. 
       if (timer.ElapsedMilliseconds >= 1000) 
       { 
        fps = frames; 
        frames = 0; 
        frameCapCounter = 0; 
        timer.Restart(); 
       } 

       Update(); 
       LateUpdate(); 

       if (canRender) 
        Render(); 
       else 
       { 
        Thread.Sleep(1); 
       } 
      } 
     } 
    } 
+0

从SlimDX的人(https://slimdx.org/docs/html/Managed_Message_Loop.htm)用来代替'Application.DoEvents()'此:https://blogs.msdn.microsoft.com/tmiller/ 2005/05/05 /我的,最后的后上渲染循环 - 但愿/ –

回答

0

在您的方案,而不是跟踪的时间过去了,帧渲染,等等,等等,你可以简单地入睡的毫秒数封顶你的FPS,例如:

public void Run() 
{ 
    window.Show(); 
    window.Focus(); 

    Initialize(); 

    isRunning = true; 

    while (isRunning) 
    { 
     if (window.Focused) 
     { 
      Update(); 
      LateUpdate(); 
      Render(); 
      Thread.Sleep(16); // hard cap 
     } 
    } 
} 

这样做的问题,然而,就是到睡觉,该代码的其余部分可能需要更长的时间超过16毫秒;对此,您可以进行一些性能测量并改为使用10毫秒。

的另一种方式,保持计时器主动将类似于以下内容:

public void Run() 
{ 
    window.Show(); 
    window.Focus(); 

    Initialize(); 

    isRunning = true; 

    long limit = 1000/60; 

    while (isRunning) 
    { 
     if (window.Focused) 
     { 
      timer.Restart(); 
      Update(); 
      LateUpdate(); 
      Render(); 
      while (timer.ElapsedMilliseconds < limit) { 
       Thread.Sleep(0); 
      } 
     } 
    } 
} 

这样就避免了“硬”帽,同时也考虑到一个事实,即所有的代码到睡眠可能需要长于16ms,因此如果必要的时间已过,则不进入“等待”循环。

此外,几个音符,可以考虑与自己发布的代码:

首先,如果你不使用任何Windows窗体元素(如PictureBoxButtonTextBox等),而是正在绘制的所有元素你的自我(例如打电话给Graphics.DrawLine等),你不需要拨打Application.DoEvents;这样做会导致调用线程等待,直到处理完所有其他Windows窗体消息,从而减慢渲染循环。如果您确实需要调用Application.DoEvents倒不如你Render()后做,以便在您的框架限制该函数调用的时间。

最后的任何小于approximately 20 milliseconds在非实时Windows系统,有机会,该Sleep实际上会睡久Thread.Sleep由于专用于一个线程在Windows系统上的时间片指定时。所以Thread.Sleep(1)威力睡眠时间为1毫秒,可能睡6毫秒;如果时间上限已经在14毫秒,6毫秒的睡眠时间将使您的总时间超过20毫秒,从而降低帧速率。

指定0毫秒的休眠时间,将调用线程的剩余时间片放弃到准备运行的另一个同等优先级的线程;如果没有其他线程准备好,则调用线程继续(等待0-1毫秒等待,vs 6+)。您也可以使用Thread.Yield,但在内核级别上与Sleep的行为大不相同。

希望能有所帮助。