将2D图形绘制到表单似乎会延迟/减慢我的程序

时间:2012-10-15 01:30:32

标签: c# .net graphics 2d lag

我刚刚开始学习.NET,所以很有可能这是一个很大的错误:

我正在尝试制作一个简单的乒乓球游戏,使用表格,然后使用 System :: Drawing :: Graphics类将游戏绘制到表单中。

我的代码的重要部分如下所示:

(主游戏循环):

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 fps)更新,它的速度要慢得多,所以我要做的就是每次移动桨/球更大的距离以补偿它不更新很快,这看起来非常糟糕。

简而言之,在绘制图形方面的一些事情正在大大减慢游戏速度。我觉得它与我的双缓冲有关,但我无法摆脱它,因为那会产生一些令人讨厌的闪烁。我的问题是, 我如何摆脱这种滞后?

2 个答案:

答案 0 :(得分:6)

此代码存在多个问题,可能严重影响应用性能:

  1. 每帧创建一个大的Bitmap缓冲区而不是将其丢弃
  2. 实现双缓冲,而WinForms已经很好地实现了这种行为
  3. updateGame()是递归的,但不一定是递归的
  4. 在GUI线程中调用Thread.Sleep()
  5. 代码示例,它使用单独的线程来计算游戏标记和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
            }
        }
    }
    

    可以进行更多改进(例如,仅使游戏画面的某些部分无效)。

答案 1 :(得分:2)

不要每帧都创建一个新的Bitmap

请勿使用Thread.Sleep。而是检查Timer组件(System.Windows.Forms命名空间中的组件)。