2

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)

Dreamer
  • 23
  • 4

3 Answers3

4

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

Piotr Stapp
  • 19,392
  • 11
  • 68
  • 116
  • 2
    Double buffering, the tool of games programming champions since, forever... I remember using this on my Commodore Amiga. – Jeff Watkins May 14 '13 at 11:38
  • 1
    The image is not flickering though, I kind of solved that in the GameDraw method where first I draw into an "invisible" Bitmap which I then copy onto the pictureBox.Image. The image just updates with a delay. – Dreamer May 14 '13 at 12:07
0

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.

Jeff Watkins
  • 6,343
  • 16
  • 19
  • The OP's program should be able to redraw the full screen over and over without any problem, this is what most games do after all. If they spend ages optimising the hell out of it so that only changes are drawn, yes they will see a benefit, but it'll just be hiding the true problem and it will take a lot longer to write and debug. – weston May 14 '13 at 12:59
0

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();
weston
  • 54,145
  • 21
  • 145
  • 203
  • After a build of the project with the new class in, it will appear in the tool box and can be dropped on the form like other controls. Good luck! – weston May 14 '13 at 13:02