4

私は.NETの学習を始めたばかりなので、これは大きなn00bの間違いである可能性があります:

フォームと 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) ごとに更新されていないということです。それははるかに遅くなっているため、更新されていないことを補うためにパドル/ボールを毎回より大きな距離に移動する必要があります。非常に速く、それは非常に悪く見えます。

一言で言えば、グラフィックスの描画に関しては、ゲームの速度が非常に大幅に低下しています。ダブルバッファリングと関係があるような気がしますが、それを取り除くことはできません。私の質問は、どうすればこのラグを取り除くことができますか?

4

2 に答える 2

6

このコードには、アプリのパフォーマンスに大きな影響を与える可能性があるいくつかの問題があります。

  1. Bitmapフレームごとに大きなバッファを作成し、破棄しない
  2. WinForms が既にこの動作の優れた実装を持っている間にダブル バッファリングを実装する
  3. updateGame() は再帰的ですが、再帰的である必要はありません
  4. Thread.Sleep()GUI スレッドでの呼び出し

別のスレッドを使用してゲーム ティックと 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
        }
    }
}

さらに改善することもできます (たとえば、ゲーム画面の一部のみを無効にするなど)。

于 2012-10-15T02:33:56.697 に答える
2

Bitmapフレームごとに新しいものを作成しないでください。

使用しないでくださいThread.SleepTimer代わりに、コンポーネント (System.Windows.Forms名前空間にあるもの)をチェックアウトします。

于 2012-10-15T02:33:17.523 に答える