2016-11-28 71 views
0

В настоящее время я делаю 2D-игровой движок с использованием C#, GDI и устанавливаю простую рамку. Игра может отображать только 60 кадров в секунду.C# GDI Game Loop (счетчик FPS)

Насколько я знаю, в коде нет никаких проблем, но я бы просто хотел сделать более чистый способ рендеринга 60 кадров в секунду и не более того.

Вот мой код, любая помощь будет большим

 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 миллисекунд; к этому вы могли бы выполнить некоторые измерения производительности и сделать что-то вроде 10ms вместо этого.

Другим способом, сохраняя таймер активным будет что-то вроде следующий:

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); 
      } 
     } 
    } 
} 

Это позволяет избежать «жесткой» шапки, а также принимая во внимание тот факт, что весь код до сна может занять больше времени, чем 16 мс, таким образом, не вводить петлю ожидания, если необходимое время прошло.

Кроме того, несколько замечаний, чтобы рассмотреть с публикуемую код:

Во-первых, если вы не используете какие-либо элементы формы для Windows (например, PictureBox, Button, TextBox и т.д.), и вместо рисования все элементы (например, называть что-то вроде Graphics.DrawLine и т. д.), вам не нужно звонить Application.DoEvents; это приводит к тому, что вызывающий поток ожидает, пока все другие сообщения Windows Forms будут обработаны, тем самым замедляя цикл визуализации. Если вам действительно нужно позвонить Application.DoEvents, было бы лучше сделать это после вашего Render(), чтобы включить это время вызова функции в ваш предел кадра.

Наконец, при указании Thread.Sleep ничего менее approximately 20 milliseconds на не-систем реального времени для Windows, вероятность того, что будет на самом деле Sleep спать дольше из-за временного среза, посвященной нити в системе Windows. Таким образом, Thread.Sleep(1)может спать за 1 миллисекунду, ИЛИ он может спать 6 миллисекунд; и если тайм-аут уже равен 14 миллисекундам, 6-миллисекундный сон поставит ваше общее время на 20 миллисекунд, что замедлит вашу частоту кадров.

Задание сна 0 миллисекунд оставляет оставшуюся часть среза времени для вызывающего потока другим потоком равного приоритета, который готов к запуску; если другие потоки не готовы, то вызывающий поток продолжается (так что 0-1 миллисекунд ожидания, vs 6+). Вы также можете использовать Thread.Yield, но он отличается от Sleep на уровне ядра.

Надеюсь, что это поможет.

 Смежные вопросы

  • Нет связанных вопросов^_^