2012-10-15 32 views
4

我剛開始學習.NET所以沒準這是一個很大的n00b錯誤:繪製2D圖形到形式似乎是滯後/慢下來我的計劃

我試圖做一個簡單的乒乓遊戲,使用然後再通過系統::繪圖::圖形類將遊戲繪製到窗體上。

我的代碼顯著位是這樣的:

(主遊戲循環):

void updateGame() 
{ 
    //Update Game Elements 
    (Update Paddle And Ball) (Ex. paddle.x += paddleDirection;) 
    //Draw Game Elements 
    //Double Buffer Image, Get Graphics 
    Bitmap dbImage = new Bitmap(800, 600); 
    Graphics g = Graphics::FromImage(dbImage); 
    //Draw Background 
    g.FillRectangle(Brushes::White, 0, 0, 800, 600); 
    //Draw Paddle & Ball 
    g.FillRectangle(Brushes::Black, paddle); 
    g.FillRectangle(Brushes::Red, ball); 
    //Dispose Graphics 
    g.Dispose(); 
    //Draw Double Buffer Image To Form 
    g = form.CreateGraphics(); 
    g.DrawImage(dbImage, 0, 0); 
    g.Dispose(); 
    //Sleep 
    Thread.sleep(15); 
    //Continue Or Exit 
    if(contineGame()) 
    { 
     updateGame(); 
    } 
    else 
    { 
     exitGame(); 
    } 
} 

(表格初始化代碼)

void InitForm() 
{ 
    form = new Form(); 
    form.Text = "Pong" 
    form.Size = new Size(800, 600); 
    form.FormBorderStyle = FormBorderStyle::Fixed3D; 
    form.StartLocation = StartLocation::CenterScreen; 
    Application::Run(form); 
} 

PS。這不是確切的代碼,我只是從內存中寫出來的,所以這就是錯誤的名字或者錯誤的名字,或者是初始化表單的一些重要的代碼行來自哪裏。

這就是代碼。 我的問題是,遊戲當然不是每15毫秒更新一次(大約60幀/秒),它的運行速度要慢得多,所以我必須做的是每次移動槳/球大量距離以補償它更新速度非常快,而且看起來非常糟糕。簡而言之,繪製圖形的過程會使遊戲速度下降很多。我有一種感覺,它與我的雙緩衝有關,但我無法擺脫這種情況,因爲這會造成一些糟糕的閃爍。我的問題是, 我該如何擺脫這種滯後?

回答

6

沒有與此代碼,可以嚴重影響應用程序性能的幾個問題:

  1. 創建大Bitmap緩衝每一幀而不是處置它
  2. 實現雙緩衝,同時的WinForms已經有很大的實現這種行爲
  3. updateGame()是遞歸的,但它並不一定要在GUI線程遞歸
  4. 調用Thread.Sleep()

代碼示例,它使用單獨的線程來計數遊戲蜱和WinForms內置雙緩衝:

static class Program 
{ 
    /// <summary> 
    /// The main entry point for the application. 
    /// </summary> 
    [STAThread] 
    static void Main() 
    { 
     Application.Run(new GameWindow()); 
    } 

    class GameWindow : Form 
    { 
     private Thread _gameThread; 
     private ManualResetEvent _evExit; 

     public GameWindow() 
     { 
      Text   = "Pong"; 
      Size   = new Size(800, 600); 
      StartPosition = FormStartPosition.CenterScreen; 
      FormBorderStyle = FormBorderStyle.Fixed3D; 
      DoubleBuffered = true; 

      SetStyle(
       ControlStyles.AllPaintingInWmPaint | 
       ControlStyles.OptimizedDoubleBuffer | 
       ControlStyles.UserPaint, 
       true); 
     } 

     private void GameThreadProc() 
     { 
      IAsyncResult tick = null; 
      while(!_evExit.WaitOne(15)) 
      { 
       if(tick != null) 
       { 
        if(!tick.AsyncWaitHandle.WaitOne(0)) 
        { 
         // we are running too slow, maybe we can do something about it 
         if(WaitHandle.WaitAny(
          new WaitHandle[] 
          { 
           _evExit, 
           tick.AsyncWaitHandle 
          }) == 0) 
         { 
          return; 
         } 
        } 
       } 
       tick = BeginInvoke(new MethodInvoker(OnGameTimerTick)); 
      } 
     } 

     private void OnGameTimerTick() 
     { 
      // perform game physics here 
      // don't draw anything 

      Invalidate(); 
     } 

     private void ExitGame() 
     { 
      Close(); 
     } 

     protected override void OnPaint(PaintEventArgs e) 
     { 
      var g = e.Graphics; 
      g.Clear(Color.White); 

      // do all painting here 
      // don't do your own double-buffering here, it is working already 
      // don't dispose g 
     } 

     protected override void OnLoad(EventArgs e) 
     { 
      base.OnLoad(e); 
      _evExit = new ManualResetEvent(false); 
      _gameThread = new Thread(GameThreadProc); 
      _gameThread.Name = "Game Thread"; 
      _gameThread.Start(); 
     } 

     protected override void OnClosed(EventArgs e) 
     { 
      _evExit.Set(); 
      _gameThread.Join(); 
      _evExit.Close(); 
      base.OnClosed(e); 
     } 

     protected override void OnPaintBackground(PaintEventArgs e) 
     { 
      // do nothing 
     } 
    } 
} 

甚至更​​改進可以由(例如,無效的遊戲畫面的只份)。

+0

太棒了!我實施了你的戰術,我得到了一個很好的FPS,幾乎沒有任何閃爍!非常感謝! – Aaron

2

請勿每幀創建一個新的Bitmap

請勿使用Thread.Sleep。請檢查Timer組件(System.Windows.Forms命名空間中的那個組件)。

+0

我試過了定時器,但它每55毫秒只調用一次我的函數,這給我一個糟糕的FPS – Aaron

+0

@ Aaronman8:嘗試使用'timeBeginPeriod'使定時器和睡眠更準確。 (但是你不需要'timeBeginPeriod(1)',5毫秒的精度應該是足夠的) –

+0

好的,我使用了定時器,並且還添加了雙緩衝,在下面的條目中建議使用「max」。它現在運行良好,我唯一的問題是,雖然閃爍少得多,我仍然得到它每秒一次...有什麼辦法可以完全刪除它? – Aaron