C#更快的方式来做精灵动画

时间:2013-11-05 18:17:11

标签: c# animation sprite

我正在使用Winforms应用程序并可以使用一些建议 我有几百个50x50的精灵随着时间的推移在2000x2000的比赛场地上移动。 最初我创建了它,精灵是由progamatically生成的图片框添加到表单并移动。它完成了工作,但它是闪烁而且很慢。

经过大量的谷歌搜索,看起来像创建一个帧缓冲区并直接绘制到那个,然后将缓冲区应用到表单上的静态图像框似乎是要走的路。

所以我装备了所有这些,结束比使用图片框慢得多。这似乎是由于缓冲区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();                
}

有关更好方法的建议吗?

我尝试提前静态创建imagebuffer并重新绘制背景。这有帮助,但它仍然比使用图片框慢得多。但是,诚然,上述方法允许适当的透明度。

2 个答案:

答案 0 :(得分: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缓冲区,并且您甚至在不处理它的情况下立即将其丢弃:

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

Bitmap类实现IDisposable,您应始终将其包装在using块中,或在不再需要时调用Dispose。在您的情况下,您可能只想创建一个位图作为缓冲区,并且在处理Control时应该将其丢弃。

您犯的另一个错误是调用Refresh,这会导致同步绘制,在您的情况下会导致控件冻结。我认为你没有充分的理由这样做。使用Invalidate代替Refresh

每次绘制单个精灵时,也会调用Graphics.FromImage。所以你每帧呼叫这几百次。显然你不想这样做。你应该每次抽奖只调用一次。

答案 1 :(得分:1)

你正在初始化一个新的Bitmap(2000,2000)动画的每一步,并且永远不会重复使用它们,这将破坏垃圾收集器。而是将缓冲区的实例保存为成员变量并保存相同大小的空白精灵。在每个绘制循环开始时,将空白精灵绘制到缓冲区上,然后绘制变形虫精灵。

由于屏幕刷新率与您的绘制率之间的不一致,这仍然可能导致闪烁。要解决此问题,请使用双缓冲。这是一个简单的双缓冲绘制方法的伪C#。

private Image backBuffer = new Bitmap(2000, 2000);
private Image frontBuffer = new Bitmap(2000, 2000);
private Image clearSprite = new Bitmap(2000, 2000);

// Draws 1 frame
private void Draw()
{
  // Clear the back buffer
  backBuffer.Draw(clearSprite);

  // Draw sprites
  foreach (var sprite in sprites)
  {
    backBuffer.Draw(sprite);
  }

  // Swap buffers
  frontBuffer.Draw(backBuffer);
}

你也在运行整个动画而不控制另一个方法,所以我还建议你转到一个异步模型,你有一个负责管理缓冲区的绘图管理器,并告诉它运行它的绘图功能设定的间隔。每个变形虫应该控制动画所在的帧,并且当绘制管理器传递给它时,它应该能够将自己绘制到缓冲区。