如何使用 Graphics.Draw 高效渲染图块?

How to render tiles efficiently using Graphics.Draw?

我目前正在用 C# 制作一个基于图块的游戏,但是每次我绘制图块时它都会使用很多 CPU,并且随着图块变大(如果我让游戏全屏显示)它消耗更多。 这是我的瓷砖 class:

 public class Tiles
{
    //PRIVATE :

    //variabiles
    private int XPosition, YPosition;
    private Image Texture;
    private bool Colidable;
    private int SizeW = 32;
    private int SizeH = 32;
    private Resizer resizer = new Resizer();
    //methods




    //PUBLIC :

    //variabiles


    //methods

    //CONSTRUCTOR
    public Tiles(int _x,int _y,Image _i, int _sW = 32, int _sH = 32, bool _c = false)
    {
        XPosition = _x;//set position X
        YPosition = _y;//set position Y
        SizeW = _sW;
        SizeH = _sH;
        Texture = resizer.ResizeImage(_i, SizeW, SizeH) ;// set texture

        Colidable = _c;//set if the tile is colidable,default : false
        resizer = null;
    }

    //DRAW METHOD
    //gets graphics object to draw on, adn draws at the position of the tile
    public void Draw(Graphics _g)
    {
        _g.DrawImage(this.Texture, this.XPosition, this.YPosition);
    }

    //GET PRIVATE MEBERS
    //returns if the tile is colidable
    public bool getColidable()
    {

        return this.Colidable;

    }
}

这就是我绘制方块的方式:

private void DrawMap(Graphics _g)
    {
        //CALLS THE DRAW METHOD OF EACH TILE
       for (int i = 0; i < MAP_WIDTH; i++)
        {
            for (int j = 0; j < MAP_HEIGHT; j++)
            {
                Tile[i, j].Draw(_g);
            }

        }

    }
    bool TilesUpdate = false;


    private void _Window_Paint(object sender, PaintEventArgs e)
    {
        e.Graphics.Clear(Color.Black);

        if (isGameRunning)
        {

            DrawMap(e.Graphics);
        }
        else
        {
            FullRezolutionBtn.Draw(e.Graphics);
            BigRezolutionBtn.Draw(e.Graphics);
            NormalRezolutionBtn.Draw(e.Graphics);
        }

    }


    private void Update_Tick(object sender, EventArgs e)
    {
        Invalidate();

    }

我想提一下,地图是 20 x 20 的图块,全屏时它会占用 cpu 的大约 50%。

不确定您的大小调整器 class 是如何工作的。我认为每次调整每张图片的大小时都会出现问题。

 Texture = resizer.ResizeImage(_i, SizeW, SizeH) ;// set texture

我会像这样替换上面的行

 Texture = _i;// set texture but do not resize image now

同时更新Tile的Draw函数如下。

public void Draw(Graphics _g)
{
    //now specify the location and size of the image.
   _g.DrawImage(Texture , new Rectangle(this.XPosition, this.YPosition, SizeW, SizeH));

}

希望它能提高性能。 如果它闪烁那么你可以使用 Double Buffer

正如我在评论中提到的,方向应该是少画。一种方法是仅当与该部分相关的内容发生变化时,才使绘图的部分无效并绘制 canvas。 Windows 本身就为 controls/windows 做了这样的优化。

这是一个例子。看看 Gadget class 如何在某些 属性 更改时使它的矩形无效。然后在绘制期间,仅绘制与 e.ClipRectange 相交的矩形。这大大减少了绘图操作的数量。

using System;
using System.Drawing;
using System.Windows.Forms;

namespace Samples
{
    class Gadget
    {
        public readonly Control Canvas;

        public Gadget(Control canvas) { Canvas = canvas; }

        private Rectangle bounds;
        public Rectangle Bounds
        {
            get { return bounds; }
            set
            {
                if (bounds == value) return;
                // NOTE: Invalidate both old and new rectangle
                Invalidate();
                bounds = value;
                Invalidate();
            }
        }

        private Color color;
        public Color Color
        {
            get { return color; }
            set
            {
                if (color == value) return;
                color = value;
                Invalidate();
            }
        }

        public void Invalidate()
        {
            Canvas.Invalidate(bounds);
        }

        public void Draw(Graphics g)
        {
            using (var brush = new SolidBrush(color))
                g.FillRectangle(brush, bounds);
        }
    }


    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            var form = new Form { WindowState = FormWindowState.Maximized };
            int rows = 9, cols = 9;
            var gadgets = new Gadget[rows, cols];
            var rg = new Random();
            Color[] colors = { Color.Yellow, Color.Blue, Color.Red, Color.Green, Color.Magenta };
            int size = 64;
            var canvas = form;
            for (int r = 0, y = 8; r < rows; r++, y += size)
                for (int c = 0, x = 8; c < cols; c++, x += size)
                    gadgets[r, c] = new Gadget(canvas) { Color = colors[rg.Next(colors.Length)], Bounds = new Rectangle(x, y, size, size) };
            int paintCount = 0, drawCount = 0;
            canvas.Paint += (sender, e) =>
            {
                paintCount++;
                for (int r = 0; r < rows; r++)
                {
                    for (int c = 0; c < cols; c++)
                    {
                        if (e.ClipRectangle.IntersectsWith(gadgets[r, c].Bounds))
                        {
                            gadgets[r, c].Draw(e.Graphics);
                            drawCount++;
                        }
                    }
                }
                form.Text = $"Paint:{paintCount} Draw:{drawCount} of {(long)paintCount * rows * cols}";
            };
            var timer = new Timer { Interval = 100 };
            timer.Tick += (sender, e) =>
            {
                gadgets[rg.Next(rows), rg.Next(cols)].Color = colors[rg.Next(colors.Length)];
            };
            timer.Start();


            Application.Run(form);
        }
    }
}