0

Trying to write a simple 2D game. Done this:

public interface GameObject
{
    Bitmap Picture  { get; }
    int OffsetX     { get; }
    int OffsetY     { get; }
}

Here Picture property returns a picture that should be drawn for this current object.

public static class Game
{
    public class MapIndexer
    {
        public GameObject this[int x, int y]
        {
            get => map[y, x];
            set => map[y, x] = value;
        }
    }

    private static MapIndexer indexer;
    public static MapIndexer Map
    {
        get => indexer ?? (indexer = new MapIndexer());
    }

    static GameObject[,] map = new GameObject[,]
    {
        { null, null, null },
        { null, null, null },
        { null, null, null }
    };

    public static int MapWidth
    {
        get => map.GetLength(1);
    }
    public static int MapHeight
    {
        get => map.GetLength(0);
    }
}

A Game class that contains Map indexer and for further development. [y, x] indexing is to access Map like Cartesian coords, otherwise I get inverted dimensions. NULL in array is just a filler (i.e. grass in my game, it does nothing and just needed to fill the map, so I see no reason to create a separate class for it). Also created a simple Player class from GameObject interface which just returns a Picture to be drawn. And here is actual paint code:

private void Form1_Paint(object sender, PaintEventArgs e)
{
    for (int y = 0; y < Game.MapHeight; y++)
        for (int x = 0; x < Game.MapWidth; x++)
        {
            e.Graphics.DrawImage(Properties.Resources.Grass, new Point(x * size, y * size));
            if (Game.Map[x, y] is not null)
                e.Graphics.DrawImage(Game.Map[x, y].Picture, new Point(x * size + Game.Map[x, y].OffsetX, y * size + Game.Map[x, y].OffsetY));
        }
}

So when I do some move, I invalidate a form and get next frame. It looks weird for me, actually it is because when action is performed, the picture flashes each redraw operation. The animation for test is player move, which occurs every 200ms, which isn't much. So the question is: how to optimise drawing so it can look smooth, or my code design is actually bad at start?

EricSchaefer
  • 25,272
  • 21
  • 67
  • 103
SelfishCrawler
  • 225
  • 2
  • 12
  • 1
    The first things you have may want to do: 1. Assign the `Properties.Resources.Grass` Bitmap object to a Property/Field of your Game class. `Properties.Resources` is a Factory, so you building a new Image each time. You don't want that. 2. Enable double buffering on the *canvas* used for drawing, if you haven't. Use a different container, e.g., a PictureBox (double buffering is enabled by default), not the Form itself. 3. Invalidate only the section you need to redraw. See the overloads of the `Invalidate()` method. 4. Avoid values that jump into the code out of nowhere, as that `size`. – Jimi Dec 12 '20 at 06:51
  • What you mean at 4. about the size? – SelfishCrawler Dec 12 '20 at 06:53
  • `new Point(x * size, y * size)` <- where `size` is coming from and where it's defined is undetermined. This value should be part of the object that describes the Image(s) you use, not a Field in the Form (or whatever that is). – Jimi Dec 12 '20 at 06:57
  • The size is a field of a form indeed, and is only used in this painting event and nowhere else. Each picture is same size – SelfishCrawler Dec 12 '20 at 07:29
  • 1
    Is `Form.DoubleBuffered == true:` ? For a Form you can turn it on directly, for PBox it is already on, for other controls you can turn it on in [code](https://stackoverflow.com/questions/44185298/update-datagridview-very-frequently/44188565#44188565)(.. – TaW Dec 12 '20 at 07:42

1 Answers1

2

The effect you are experiencing is not really related to performance. What you are seeing, is most likely the effect of clearing the screen before the redraw. You should look into a concept called "double buffering".

EricSchaefer
  • 25,272
  • 21
  • 67
  • 103