Creating Tetris Using Visual C++ Windows Form. Part two.

In the first article I described the process of creating a Shape class that encapsulates the logic of the Tetris game. No we will create an form, that will present the interface of the Tetris game. The first thing is to create the form appearance. I set a background image in the properties tab in Visual Studio. After this I added a menu strip to have 3 menu items: 1. 1 2. 1 3. 1 All these things can be done in Visual Studio Form constructor by simply dragging elements from element's panel. Now we need to interact with the created Shape class to play a Tetris game. The form class needs to have these member variable:
  1. Shape^ shape;
  2. array<Int16,2>^ stock;//game board
  3. Point^ curPoint;//curent point
  4. Boolean bGame;
  5. Boolean _pause;
  6. Boolean firstView;//used to show welcome screen
  7. Int16 score;
  8. Double level;
  9. Int16 color; // current color
  10. System::Timers::Timer^ t1;
The constructor of the class initializes these variables:
  1. Form1(void)
  2. {
  3. stock = gcnew array<Int16,2>(20,10);
  4. shape = gcnew Shape();
  5. _pause = false;
  6. firstView = true;
  7. Invalidate();
  8.  
  9. SetLevel(1);
  10. InitializeComponent();
  11. //Don'tchange window size
  12. MaximizeBox = false;
  13.  
  14. }
The all drawing of the program is done in onPaint function. As you can see in the code above, there is a variable, called firstView. It's used in the onPaint function to show welcome screen:
  1. if(firstView)
  2. {
  3. System::Drawing::Font^ drawFont = gcnew System::Drawing::Font( "Arial",16 );
  4. SolidBrush^ drawBrush = gcnew SolidBrush( Color::Black );
  5. PointF drawPoint = PointF(10.0F,50.0F);
  6. grfx->DrawString("Tetris.\nChose difficulty\n and press \nF2 to play",drawFont, drawBrush,drawPoint);
  7. firstView = false;
  8. }
The next step is to set event handlers for all menu items for the game menu. A double click on a menu item in constructor creates a handler for this item. The new menu item click starts new game:
  1. System::Void newGameToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e)
  2. {
  3. firstView = false;
  4. SetLevel(1);
  5. this->t1 = gcnew System::Timers::Timer(level);
  6. this->t1->Elapsed += gcnew System::Timers::ElapsedEventHandler(this, &Form1::TimerEventProcessor);
  7. ClearStock();
  8. score=0;
  9. bGame=true;
  10. NewShape();
  11. this->Invalidate();
  12. }
A timer is started in this function. It's done to change the position of the figure from top to bottom, according to the selected level. The event handler of the timer changes the position of the shape and checks if the game is not over of a line is completed:
  1. if(bGame==true)
  2. {
  3. if(IsAtBottom())
  4. {
  5. for(Int16 i=0; i<4; i++)
  6. for(Int16 j=0; j<4; j++)
  7. {
  8. if(shape->cells[i,j]==true)
  9. stock[curPoint->Y/20+i,curPoint->X/20+j]=color;
  10. }
  11. NewShape();
  12. if(IsAtBottom())
  13. {
  14. bGame=false;
  15. }
  16. this->Invalidate();
  17. CheckForLine();
  18. return;
  19. }
  20. curPoint->Y+=20;
  21. this->Invalidate();
  22. }
  23. else if(bGame==false)
  24. {
  25. this->t1->Stop();
  26. MessageBox::Show("You lost !\rYour score is : "+score);
  27. }
The new shape function produces a new shape of the random type:
  1. void NewShape(void) // Novaia figura
  2. {
  3. t1->Start();
  4. curPoint = gcnew Point(0,60);
  5. Random^ rand = gcnew Random();
  6. ShapeType t = ShapeType(rand->Next(7));
  7. shape->NewShape(t);
  8. color=rand->Next(6);
  9. };
This function changes the position of the shape to the initial. Reset's its color and type. The event handler for pressed key checks the possibility to move shape to left or right, pause and restart the game,rotates the shape and invalidates the region,where the shape is located:
  1. System::Void Form1_KeyDown(System::Object^ sender, System::Windows::Forms::KeyEventArgs^ e)
  2. {
  3. switch(e->KeyCode)
  4. {
  5. case Keys::Up :
  6. if(IsAtBottom()||CheckRight()) return;
  7. shape->Rotate();
  8. this->Invalidate(GetReg());
  9. break;
  10. case Keys::Left:
  11. if(CheckLeft()) return;
  12. curPoint->X-=20;
  13. this->Invalidate(GetReg());
  14. break;
  15. case Keys::Right:
  16. if(CheckRight()) return;
  17. curPoint->X+=20;
  18. this->Invalidate(GetReg());
  19. break;
  20. case Keys::Down:
  21. while(!IsAtBottom()){
  22. curPoint->Y+=20;
  23. this->Invalidate(GetReg());
  24. }
  25.  
  26. break;
  27. case Keys::Pause:
  28. if(!_pause){
  29. t1->Stop();
  30. _pause = true;
  31. }
  32. else{
  33. t1->Start();
  34. _pause = false;
  35. }
  36. this->Invalidate();
  37. break;
  38. default :
  39. break;
  40. }
  41. }
The checks of the possibilities to move are performed by the following functions thanks to the coordinated of the current position of the shape:
  1. Boolean CheckLeft(void) // vojmojnosti idti nalevo
  2. {
  3. for(Int16 i=0; i!=4; i++)
  4. {
  5. for(Int16 j=0; j!=4; j++)
  6. {
  7. if(shape->cells[i,j]==true)
  8. {
  9. if((curPoint->X/20+j)<=0 || stock[curPoint->Y/20+i,curPoint->X/20+j-1]!=-1)
  10. return true;
  11. }
  12. }
  13. }
  14. return false;
  15. }
  16. Boolean CheckRight(void) // vojmojnosti dvigatsia vpravo
  17. {
  18. for(Int16 i=0; i != 4; i++)
  19. {
  20. for(Int16 j=0; j!=4; j++)
  21. {
  22. if(shape->cells[i,j]==true)
  23. {
  24. if((curPoint->X/20+j)>=9 || stock[curPoint->Y/20+i,curPoint->X/20+j+1]!=-1)
  25. return true;
  26. }
  27. }
  28. }
  29. return false;
  30. }
  31.  
We need to add points and clear a line when is filled. This check is done here in the following way:
  1. void CheckForLine(void)
  2. {
  3. Int16 i,j,k,c=0;
  4. Boolean flag;
  5.  
  6. for(i=0; i<20; i++)
  7. {
  8. flag=true;
  9. for(j=0; j<10; j++)
  10. flag &= (stock[i,j]!=-1);
  11.  
  12. if(flag==true && j!=0)
  13. {
  14. c++;
  15. score++;
  16.  
  17. for(k=i; k>0; k--)
  18. {
  19. for(j=0; j<10; j++)
  20. {
  21. stock[k,j]=stock[k-1,j];
  22. }
  23. }
  24. }
  25. }
  26. for(k=0; k<c; k++)
  27. for(j=0; j<10; j++)
  28. stock[k,j]=-1;
  29. this->Invalidate();
  30. }
And the last check is to check, if the Shape is in the bottom:
  1. Boolean IsAtBottom(void)
  2. {
  3. for(Int16 i=0; i<4; i++)
  4. {
  5. for(Int16 j=0; j<4; j++)
  6. {
  7. if(shape->cells[i,j]==true)
  8. {
  9. if((curPoint->Y/20+i)>=19 || stock[curPoint->Y/20+i+1,curPoint->X/20+j]!=-1)
  10. return true;
  11. }
  12. }
  13. }
  14. return false;
  15. }
The OnPaint function simply draws the rectangles of different colors according to the values, that are in stock:
  1. Graphics^ grfx = pea->Graphics;
  2. Int16 i,j;
  3. SolidBrush^ brush = gcnew SolidBrush(ShapeColor(color));
  4.  
  5. for(i=0; i<4; i++)
  6. {
  7. for(j=0; j<4; j++)
  8. {
  9. if(shape->cells[i,j]==true)
  10. grfx->FillRectangle(brush,curPoint->X+j*size, curPoint->Y+i*size,size,size);
  11. }
  12. }
  13.  
  14. for(i=0; i<20; i++)
  15. {
  16. for(j=0; j<10; j++)
  17. {
  18. if(stock[i,j]!=-1)
  19. {
  20. brush->Color = ShapeColor(stock[i,j]);
  21. grfx->FillRectangle(brush,j*size,i*size,size,size);
  22. }
  23. }
  24. }
  25.  
  26. if(_pause)
  27. {
  28. System::Drawing::Font^ drawFont = gcnew System::Drawing::Font( "Arial",20 );
  29. SolidBrush^ drawBrush = gcnew SolidBrush( Color::Black );
  30. PointF drawPoint = PointF(10.0F,150.0F);
  31. grfx->DrawString("Game paused.\nPress \"Pause\"\n to continue",drawFont, drawBrush,drawPoint);
  32. }
Here are the complete details about how to implement the Tetris user interface as a Visual c++ Windows Forms Application.You can download the complete source code of this Visual Studio 2010 Project.

Add new comment