Develop Tetris Brick game using basic Windows Form and C# Dotnet codes

Develop Tetris Brick game using basic Windows Form and C# Dotnet codes

Hello friends, Let’s see how we can create a Tic Tac Toe puzzle game (3X3) step by step using Windows Form and C# DotNet in Visual Studio without using any complex/advance codes neither external libraries.


Prerequisites
  • Basic c# programming knowledge
  • Basic windows form application development knowledge
  • Visual studio 2010+  


Step 1.

Start a new Windows Form Project in Visual Studio which will provide you the default form template and add a picture box as shows in the below screenshot. 

Develop Tetris Brick game using basic Windows Form and C# Dotnet codes


Step 2.

Add code to load canvas using bitmap/graphics classes and bind the bitmap in the picture box. (You can go to the code behind by right click on the form and select view code menu)

public Form1()
{
    InitializeComponent();
            
    loadCanvas();
}
        
Bitmap canvasBitmap;
Graphics canvasGraphics;
int canvasWidth = 15;
int canvasHeight = 20;
int[,] canvasDotArray;
int dotSize = 20;

private void loadCanvas()
{
    // Resize the picture box based on the dotsize and canvas size
    pictureBox1.Width = canvasWidth * dotSize;
    pictureBox1.Height = canvasHeight * dotSize;

    // Create Bitmap with picture box's size
    canvasBitmap = new Bitmap(pictureBox1.Width, pictureBox1.Height);

    canvasGraphics = Graphics.FromImage(canvasBitmap);

    canvasGraphics.FillRectangle(Brushes.LightGray, 0, 0, canvasBitmap.Width, canvasBitmap.Height);

    // Load bitmap into picture box
    pictureBox1.Image = canvasBitmap;
            
    // Initialize canvas dot array. by default all elements are zero
    canvasDotArray = new int[canvasWidth, canvasHeight];
}


As in the code we maintain a canvas dot array to understand which are blocks are filled with shapes. The following picture will give more idea on this logic.

Develop Tetris Brick game using basic Windows Form and C# Dotnet codes


Let's run the application and see if the canvas loads correctly 

Develop Tetris Brick game using basic Windows Form and C# Dotnet codes


Step 3.

Add Shape and Handler Classes to store/manage shapes.
This shape class will allow us to create/store shapes using two dimensional arrays.

namespace Tetris
{
    class Shape
    {
        public int Width;
        public int Height;
        public int[,] Dots;
    }
}


The following shape handler class will load shapes array in the constructor and have method to get a shape from the array on a random basis.


using System;

namespace Tetris
{
    static class ShapesHandler
    {
        private static Shape[] shapesArray;
        
        // static constructor : No need to manually initialize
        static ShapesHandler()
        {
            // Create shapes add into the array.
            shapesArray = new Shape[]
                {
                    new Shape {
                        Width = 2,
                        Height = 2,
                        Dots = new int[,]
                        {
                            { 1, 1 },
                            { 1, 1 }
                        }
                    },
                    new Shape {
                        Width = 1,
                        Height = 4,
                        Dots = new int[,]
                        {
                            { 1 },
                            { 1 },
                            { 1 },
                            { 1 }
                        }
                    },
                    new Shape {
                        Width = 3,
                        Height = 2,
                        Dots = new int[,]
                        {
                            { 0, 1, 0 },
                            { 1, 1, 1 }
                        }
                    },
                    new Shape {
                        Width = 3,
                        Height = 2,
                        Dots = new int[,]
                        {
                            { 0, 0, 1 },
                            { 1, 1, 1 }
                        }
                    },
                    new Shape {
                        Width = 3,
                        Height = 2,
                        Dots = new int[,]
                        {
                            { 1, 0, 0 },
                            { 1, 1, 1 }
                        }
                    },
                    new Shape {
                        Width = 3,
                        Height = 2,
                        Dots = new int[,]
                        {
                            { 1, 1, 0 },
                            { 0, 1, 1 }
                        }
                    },
                    new Shape {
                        Width = 3,
                        Height = 2,
                        Dots = new int[,]
                        {
                            { 0, 1, 1 },
                            { 1, 1, 0 }
                        }
                    }
                    // new shapes can be added here..
                };
        }
        
        // Get a shape form the array in a random basis
        public static Shape GetRandomShape()
        {
            var shape = shapesArray[new Random().Next(shapesArray.Length)];

            return shape;
        }
    }
}


Step 4.

Now let's go back to the form class to add method to consume the random shape getter method and adjust the currentX variable to make the shape drawn in the center position

int currentX;
int currentY;

private Shape getRandomShapeWithCenterAligned()
{
    var shape = ShapesHandler.GetRandomShape();
            
    // Calculate the x and y values as if the shape lies in the center
    currentX = 7;
    currentY = -shape.Height;

    return shape;
}


Code to move the shape one step down/side and draw it into the canvas using a different bitmap/graphics classes.

// returns if it reaches the bottom or touches any other blocks
private bool moveShapeIfPossible(int moveDown = 0, int moveSide = 0)
{
    var newX = currentX + moveSide;
    var newY = currentY + moveDown;

    // check if it reaches the bottom or side bar
    if (newX < 0 || newX + currentShape.Width > canvasWidth
        || newY + currentShape.Height > canvasHeight)
        return false;

    // check if it touches any other blocks 
    for (int i = 0; i < currentShape.Width; i++)
    {
        for (int j = 0; j < currentShape.Height; j++)
        {
            if (newY + j > 0 && canvasDotArray[newX + i, newY + j] == 1 && currentShape.Dots[j, i] == 1)
                return false;
        }
    }

    currentX = newX;
    currentY = newY;

    drawShape();

    return true;
}

Bitmap workingBitmap;
Graphics workingGraphics;

private void drawShape()
{
    workingBitmap = new Bitmap(canvasBitmap);
    workingGraphics = Graphics.FromImage(workingBitmap);

    for (int i = 0; i < currentShape.Width; i++)
    {
        for (int j = 0; j < currentShape.Height; j++)
        {
            if (currentShape.Dots[j, i] == 1)
                workingGraphics.FillRectangle(Brushes.Black, (currentX + i) * dotSize, (currentY + j) * dotSize, dotSize, dotSize);
        }
    }

    pictureBox1.Image = workingBitmap;
}


Code to update the canvas dot array and do the game over check once the shape reaches the bottom or touches other shapes.

private void updateCanvasDotArrayWithCurrentShape()
{
    for (int i = 0; i < currentShape.Width; i++)
    {
        for (int j = 0; j < currentShape.Height; j++)
        {
            if (currentShape.Dots[j, i] == 1)
            {
                checkIfGameOver();

                canvasDotArray[currentX + i, currentY + j] = 1;
            }
        }
    }
}

private void checkIfGameOver()
{
    if (currentY < 0)
    {
        timer.Stop();
        MessageBox.Show("Game Over");
        Application.Restart();
    }
}


Add a timer and consume the above methods to make the shapes dropping back to back and do the game end check.


Shape currentShape;
Timer timer = new Timer();
public Form1()
{
    InitializeComponent();
            
    loadCanvas();

    currentShape = getRandomShapeWithCenterAligned();

    timer.Tick += Timer_Tick;
    timer.Interval = 500;
    timer.Start();
}

private void Timer_Tick(object sender, EventArgs e)
{
    var isMoveSuccess = moveShapeIfPossible(moveDown: 1);

    // if shape reached bottom or touched any other shapes
    if (!isMoveSuccess)
    {
     // copy working image into canvas image
        canvasBitmap = new Bitmap(workingBitmap);

        updateCanvasDotArrayWithCurrentShape();
                
     // get next shape
        currentShape = getRandomShapeWithCenterAligned();
    }
}


Not let’s run the application

Develop Tetris Brick game using basic Windows Form and C# Dotnet codes


Step 5.

Now let's add  methods to turn the shape and rollback the turn. The rolling back happens once the player tries to turn the shape but that move is not available because of it reaches the bottom or touches other shapes.

namespace Tetris
{
    class Shape
    {
        public int Width;
        public int Height;
        public int[,] Dots;

        private int[,] backupDots;

        public void turn()
        {
            // back the dots values into backup dots
            // so that it can be simply used for rolling back
            backupDots = Dots;

            Dots = new int[Width, Height];

            for (int i = 0; i < Width; i++)
            {
                for (int j = 0; j < Height; j++)
                {
                    Dots[i, j] = backupDots[Height - 1 - j, i];
                }
            }

            var temp = Width;
            Width = Height;
            Height = temp;
        }

        // the rolling back occurs when player rotating the shape
        // but it will touch other shapes and needs to be rolled back
        public void rollback()
        {
            Dots = backupDots;

            var temp = Width;
            Width = Height;
            Height = temp;
        }
    }
}


Add keystroke events to move and turn shape. On clicking the right/left arrow keys the shape should move side wise, clicking on the top arrow key should turn the shape and clicking on the down arrow key should make the shape move faster.

public Form1()
{
    // previous codes....
            
    this.KeyDown += Form1_KeyDown;
}

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
    var verticalMove = 0;
    var horizontalMove = 0;

    // calculate the vertical and horizontal move values
    // based on the key pressed
    switch (e.KeyCode)
    {
        // move shape left
        case Keys.Left:
            verticalMove--;
            break;
                
        // move shape right
        case Keys.Right:
            verticalMove++;
            break;

        // move shape down faster
        case Keys.Down:
            horizontalMove++;
            break;

        // rotate the shape clockwise
        case Keys.Up:
            currentShape.turn();
            break;

        default:
            return;
    }

    var isMoveSuccess = moveShapeIfPossible(horizontalMove, verticalMove);

    // if the player was trying to rotate the shape, but
    // that move was not possible - rollback the shape
    if (!isMoveSuccess && e.KeyCode == Keys.Up)
        currentShape.rollback();
}


Step 6.

Now let's add new controls in the form in order to show the next shape, current score and level. (Picture box to show next shape and labels to show score and game level)

Develop Tetris Brick game using basic Windows Form and C# Dotnet codes


Add code to clear all the rows which are filled with shapes and increment the score, level and speed values based on the cleared rows count.

int score;

public void clearFilledRowsAndUpdateScore()
{
    // check through each rows
    for (int i = 0; i < canvasHeight; i++)
    {
        int j;
        for (j = canvasWidth - 1; j >= 0; j--)
        {
            if (canvasDotArray[j, i] == 0)
                break;
        }

        if (j == -1)
        {
            // update score and level values and labels
            score++;
            label1.Text = "Score: " + score;
            label2.Text = "Level: " + score / 10;
            // increase the speed 
            timer.Interval -= 10;

            // update the dot array based on the check
            for (j = 0; j < canvasWidth; j++)
            {
                for (int k = i; k > 0; k--)
                {
                    canvasDotArray[j, k] = canvasDotArray[j, k - 1];
                }

                canvasDotArray[j, 0] = 0;
            }
        }
    }

    // Draw panel based on the updated array values
    for (int i = 0; i < canvasWidth; i++)
    {
        for (int j = 0; j < canvasHeight; j++)
        {
            canvasGraphics = Graphics.FromImage(canvasBitmap);
            canvasGraphics.FillRectangle(
                canvasDotArray[i, j] == 1 ? Brushes.Black : Brushes.LightGray,
                i * dotSize, j * dotSize, dotSize, dotSize
                );
        }
    }

    pictureBox1.Image = canvasBitmap;
}

Add code to get and show the next shape in the side panel

Bitmap nextShapeBitmap;
Graphics nextShapeGraphics;

private Shape getNextShape()
{
    var shape = getRandomShapeWithCenterAligned();

    // Codes to show the next shape in the side panel
    nextShapeBitmap = new Bitmap(6 * dotSize, 6 * dotSize);
    nextShapeGraphics = Graphics.FromImage(nextShapeBitmap);

    nextShapeGraphics.FillRectangle(Brushes.LightGray, 0, 0, nextShapeBitmap.Width, nextShapeBitmap.Height);

    // Find the ideal position for the shape in the side panel
    var startX = (6 - shape.Width) / 2;
    var startY = (6 - shape.Height) / 2;

    for (int i = 0; i < shape.Height; i++)
    {
        for (int j = 0; j < shape.Width; j++)
        {
            nextShapeGraphics.FillRectangle(
                shape.Dots[i, j] == 1 ? Brushes.Black : Brushes.LightGray,
                (startX + j) * dotSize, (startY + i) * dotSize, dotSize, dotSize);
        }
    }

    pictureBox2.Size = nextShapeBitmap.Size;
    pictureBox2.Image = nextShapeBitmap;

    return shape;
}


Update the form constructor and timer click event methods to clear the filled rows and show the next shape. Recently it was assigning a random shape from the list to the current shape variable. Here after it will assign the random shape in to the next shape variable and then it will be assigned to the current shape variable on dropping the next shape.

Shape currentShape;
Shape nextShape;

public Form1()
{
    // Previous Code....

    currentShape = getRandomShapeWithCenterAligned();
    nextShape = getNextShape();

    // Previous Code....
}


private void Timer_Tick(object sender, EventArgs e)
{
    // Previous Code....

    // if shape reached bottom or touched any other shapes
    if (!isMoveSuccess)
    {
     // Previous Code....

        // get next shape
        currentShape = nextShape;
        nextShape = getNextShape();
                
        clearFilledRowsAndUpdateScore();
    }
}


Let’s run the final code

Develop Tetris Brick game using basic Windows Form and C# Dotnet codes


You can try adding new interesting shapes in the shape handler class. 





Enjoy Coding…!!!



Comments

  1. Is it easily possible to have different colors for the different shapes?

    ReplyDelete
    Replies
    1. Yes, we can add a color property in the Shape class, which gets assigned with random colors from the constructor method. And when we draw the shape use that property instead of Black color.

      Delete
  2. Very well written about develop tetris brick game. If you want videos of brick game, please download vidmate.You can not download this app from play store but from other website. You must be wondering why vidmate.app has been removed from play store despite being so popular. Play Store had some policy of its own which Vidmate app has removed from play store for not following this app. But no problem, you can enjoy by downloading this app from other website. This app has all the facilities for your entertainment be it anything from TV to online news, movies, viral videos etc. All the facilities are absolutely free for you. You can also download brick game and Vidmate from 9apps

    ReplyDelete
  3. why are you using a separate class to implement the rotation of the shape?

    ReplyDelete

Post a Comment