Question

I'm programming a tetris clone for my C# school project. I am using Microsoft Visual Studio 2012. The game itself is implemented as a two dimensional array of blocks(List of Lists of blocks) and every block has its own texture (bmp image). I am drawing the whole array onto a PictureBox control and this is where the problem starts. When updating the image on the PictureBox (moving/rotating the active shape) the game slightly lags. I tried to draw on a Panel control instead but the result was the same. I have a rough idea what might cause the lag but I don't know exactly how to get rid of it.

This is the draw method of the game "grid":

public void Draw(Graphics g)
{
   Brush brush;
   Font font = new System.Drawing.Font( "Arial", 5);
   for (int i = 0; i < Width; i++)
     for (int j = 0; j < Height; j++)
     {
          brush = new TextureBrush(Blocks[i][j].Texture);
          if (Blocks[i][j].Occupied==true)
             g.FillRectangle(brush, i * 20, j * 20, i * 20 + Blocks[i][j].Texture.Width, j * 20 + Blocks[i][j].Texture.Height); 
     }
}

This is the draw method of the active tetromino:

public void Draw(Graphics g)
{
    Brush brush = new TextureBrush(Blocks[0].Texture);
    foreach (FullBlock b in Blocks)
       g.FillRectangle(brush, b.x * 20, b.y * 20,b.Texture.Width, b.Texture.Height);
}

The game itself then use both of them (double buffering attempt):

public void GameDraw(PictureBox p)
{
    Graphics g = Graphics.FromImage(gb);
    gameGrid.Draw(g);
    PlayingShape.Draw(g);
    p.Image = gb;
    p.Refresh();
}

where "gb" is a private Bitmap variable I create just once in the class constructor (to reduce (unsuccessfully) the lag).

The GameDraw method is called whenever the state of the game is changed (e.g. moving/rotating the active tetromino and every "gravity" tick)

Was it helpful?

Solution 2

No need for picture box, add your own control:

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

namespace TetrisGame
{
    public sealed class TetrisControl : Control
    {
        private TheBlockType[][] blocks = ...;

        protected override void OnPaint(PaintEventArgs e)
        {
            //draw your stuff here direct to the control, no buffers in the middle
            //if that causes flickering, turn on double buffering, but don't bother doing it yourself
            //this is your existing draw method:
            Draw(e.Graphics);
        }
    }
}

Then on every tick or movement, do not call paint, just invalidate the control:

tetris.Invalidate();

OTHER TIPS

You need Double buffering, which you did not set. Quoting MSDN:

Double buffering uses a memory buffer to address the flicker problems associated with multiple paint operations. When double buffering is enabled, all paint operations are first rendered to a memory buffer instead of the drawing surface on the screen

You can enable it using Control.DoubleBuffered property

Also, think out of the box... Rather than doing a full grid scan, you could make each of your shapes part of a linked-list and redraw them based on their position in the grid... Until your grid completely fills, you'd be doing less scanning.

Or, consider only redrawing what you need to redraw, i.e. a block drops near the top, no need to completely redraw the full grid.

Optimisation is what separates us from the animals. Apart from the platypus, who is an optimal creature.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top