0

私はWinformsアプリに取り組んでおり、アドバイスが必要です。
時間の経過とともに 2000x2000 の競技場を動き回る 50x50 のスプライトが数百個あります。最初は、フォームに追加されて動き回る、プログラム的に生成された画像ボックスであるスプライトを使用して作成しました。仕事は完了しましたが、ちらつきと遅さがありました。

かなりのグーグル検索の後、フレーム バッファを作成してそこに直接描画し、そのバッファをフォーム上の静止画像ボックスに適用する方法のように見えました。

だから私はこれをすべて装備し、ピクチャボックスを使用するよりもはるかに遅くなります. 2000x2000のバッファのサイズによるものと思われます(毎回バッファを作成するのに約100msかかります。)

画面を描画するためのコード:

private void animateAmoebas()
{
    for (int animationStep = 0; animationStep < 100; animationStep = animationStep + animationStepSize)
        {

        Image buffer = new Bitmap(2000, 2000);              
        buffer = imageBKG; //Redraw the grid pattern.                   
        foreach (Amoeba _Amoeba in amoebaPool)//Ameboa is a class object that has AI in it to detirmine the actions of the Amoeba.
            {
                //PBL (PictureBoxLoader) is an object that contains the sprite image, plus the cordinates for that sprite in that frame.
                pbl = _Amoeba.animateSprite(animationStep,pbl);
                drawSprite(pbl, buffer);//Draw the sprite to the buffer
            }               
            refreshScreen(buffer);//Copy the buffer to the picturebox
        }
}


private void drawSprite(PictureBoxLoader pbLoader, Image _buffer) 
{
    using (Graphics formGraphics = Graphics.FromImage(_buffer))
    {
        Point imgPoint = new Point(pbLoader.imgX, pbLoader.imgY);
        formGraphics.DrawImageUnscaled(pbLoader.imgImage, imgPoint);
    }
}

private void refreshScreen(Image _image)
{         
        pictureBox_BKG.Image = _image;
        this.Refresh();                
}

これを行うためのより良い方法についての提案はありますか?

事前にイメージバッファを作成し、背景を再描画するだけのスタティックを試してみました。これは役に立ちますが、ピクチャ ボックスを使用するよりも劇的に遅くなります。ただし、確かに上記の方法で適切な透明度が得られます。

4

2 に答える 2

2

PictureBox はまったく使用しないでください。から派生しControlてオーバーライドするだけOnPaintです。内のバッファ イメージにOnPaint描画し、そのイメージをコントロールに描画できます。

public class SpriteCanvas : Control
{
    private const int AnimationSteps = 100;
    private const int AnimationStepSize = 4;

    private System.Windows.Forms.Timer _timer;
    private Bitmap _buffer;
    private int _animationStep = 0;

    public SpriteCanvas()
    {
        _buffer = new Bitmap(2000, 2000, PixelFormat.Format32bppPArgb);
        _timer = new System.Windows.Forms.Timer();
        _timer.Interval = 10;

        _timer.Tick += (s, e) =>
        {
            _animationStep += AnimationStepSize;

            if (_animationStep > AnimationSteps)
                _animationStep = 0;

            this.Invalidate();
        };

        _timer.Start();
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        using (var g = Graphics.FromImage(_buffer))
        {
            // draw sprites based on current _animationStep value
            // g.DrawImage(...)
        }

        e.Graphics.DrawImage(_buffer, new Rectangle(0, 0, _buffer.Width, _buffer.Height), new Rectangle(0, 0, _buffer.Width, _buffer.Height), GraphicsUnit.Pixel);
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);
        _timer.Dispose();
        _buffer.Dispose();
    }
}

コードには他にも多くの問題があります。まず第一に、UI スレッドですべてを描画していると仮定しています。これはノーノーです。Invalidate再描画するときは、コントロールを呼び出す必要があります。タイマーでこれを行う必要があります。

また、ループの各反復で新しい画像バッファーを作成しており、破棄することさえせずにすぐに破棄しています。

Image buffer = new Bitmap(2000, 2000);              
buffer = imageBKG; //Redraw the grid pattern. 

Bitmapクラスは実装されており、不要になった場合IDisposableは常にusingブロックでラップするか、それを呼び出す必要があります。Disposeあなたの場合、おそらくバッファとして機能するビットマップを1つだけ作成したいので、破棄するときにそれを破棄する必要がありますControl

あなたが犯しているもう1つの間違いは、呼び出しですRefresh。これにより、同期ペイントが発生し、場合によってはコントロールがフリーズします。あなたがこれを行う正当な理由があるとは思いません。Invalidateの代わりに使用しRefreshます。

Graphics.FromImageまた、単一のスプライトを描画するたびに呼び出します。したがって、これをフレームごとに何百回も呼び出しています。明らかに、あなたはこれをしたくありません。これは、ドローごとに 1 回だけ呼び出す必要があります。

于 2013-11-05T18:32:04.447 に答える