Thread: Not really much of a game programmer, yet making a tetris clone :D

  1. #1
    Registered User Swarvy's Avatar
    Join Date
    Apr 2008
    Location
    United Kingdom
    Posts
    195

    Exclamation Not really much of a game programmer, yet making a tetris clone :D

    Hi guys. This is my first post here Anyway, I'm working on a console tetris clone (I bet you get alot of people on here who do these little projects, lol), but I have ran into a touch of difficulty with one or two aspects of it. (This is my first game. I don't usually program games, hence why I'm having a few problems).

    So far, I have just focused on functionality, rather than making it look nice... I will be doing that at a later stage. I have programmed it so that the w,s,a and d keys move just the one block around the 'allowed' area (obviously it wont work exactly like that when it is done, but I just wanna get something which can move about on the screen first). Below is my code atm. The problem I am having (among others), is that my block won't move Also, I'm not entirely sure how I will do the rotate part, so if anyone has any ideas, I would appreciate them. I'm programming in Dev-C++ in windows. I usually do most of my programming in Linux, but nm.

    It is not as efficient as it really could be, but again, I will change that when I have something which works entirely first.

    Code:
    // INCLUDES //
    #include <cstdlib>
    #include <iostream>
    #include <windows.h>
    #include <conio.h>
    
    // DEFINES //
    #define WIN32_LEAN_AND_MEAN
    #define WIDTH      50
    #define HEIGHT     54
    
    // Function Prototypes //
    void game_menu();     // Menu function of the program
    void game_init();     // Game init function
    void game_grid();     // Function draws the tetris grid
    void game_grid_clear();  // This function clears the interior of the grid
    void game_previous_blocks();
    void game_view_grid();
    void game_keyboard(); // Take keyboard input and processing it
    void game_translate(); // Performs the standard, one space down thing
    void game_graphics_update();       // Update the graphics
    void game_main();     // This is the main game loop function. This is where most of the work is done
    void game_close();    // This function closes the application
    void game_controls(); // This displays the controls for playing the game.
    void game_draw_block();
    int game_still_running(); // Tests if the program is still in 'run' mode
    
    // Globally declared variables //
    HANDLE hOutput;
    int Option_number,i,j;
    char grid[WIDTH][HEIGHT];     // This is the game array, and will record where the blocks are
    char player_name;
    int RUNNING;
    
    using namespace std;
    
    // Main Program //
    int main(int argc, char *argv[])
    {
        hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
        SetConsoleTextAttribute(hOutput,
                                FOREGROUND_GREEN|FOREGROUND_INTENSITY);
        game_menu();
        
        system("Pause");
        return EXIT_SUCCESS;
    }
    
    void game_grid_clear()
    {
         COORD Position;
         int i,j;
         for(i=0; i<WIDTH; i++)
         {
                  for(j=0; j<HEIGHT; j++)
                  {
                           Position.X = i;
                           Position.Y = j;
                           SetConsoleCursorPosition(hOutput, Position);
                           cout << " ";
                  }
         }
    }
    
    void game_previous_blocks()
    {
         // This function draws all the previously dropped blocks
         // It also gets rid of any full lines of blocks
         
    }
    
    void game_controls()
    {
         system("cls");
         cout << "\n\t\tControls:\n\n";
         cout << "\tUp Arrow    : Rotate piece clockwise\n";
         cout << "\tDown Arrow  : Rotate piece anticlockwise\n";
         cout << "\tLeft Arrow  : Move piece to the left\n";
         cout << "\tRight Arrow : Move piece to the right\n";
         cout << "\tDelete      : Make the piece drop quicker\n";
         
         cout << "\n\t";
         system("Pause");
         game_menu();
    }
    
    void game_menu()
    {
         system("cls");
         cout << "\n\t\tTetris Clone\n\n";
         cout << "\tOptions:\n\t1. Play\n\t2. Controls\n\n\tOption Number>>";
         cin >> Option_number;
         
         if(Option_number == 1)
         {
                          game_init();
                          }
                          else if(Option_number == 2)
                          {
                               game_controls();
                               }
    }
    
    void game_grid()
    {
         // Build game grid on the screen
         int i;
         COORD Position;
         system("cls");
         for(i=0; i<HEIGHT; i++)
         {
                  Position.X = WIDTH;
                  Position.Y = i;
                  SetConsoleCursorPosition(hOutput,
                                     Position);
                  cout << "|";
         }
         
         for(i=0; i<WIDTH; i++)
         {
                  Position.X = i;
                  Position.Y = HEIGHT;
                  SetConsoleCursorPosition(hOutput,
                                     Position);
                  cout << "_";
         }
    }
    
    void game_init()
    {
         system("cls");
         // Initial locally declared variables
         char start;
         int i, j;
         COORD Position1, Position2;
         
         // That the player's name
         cout << "\n\t\tTetris Clone\n\n";
         cout << "Please enter the player's name>>";
         cin >> player_name;
         
         // Draw the game grid
         game_grid();
         
         // Ask if the player is ready to begin
         Position1.X = 0;
         Position1.Y = 56;
         
         Position2.X = 0;
         Position2.Y = 57;
         
         start = 'z';
         
         while(start !='y')
         {
                     for(i=0; i<WIDTH; i++)
                     {
                              Position2.X = i;
                              SetConsoleCursorPosition(hOutput, Position2);
                              cout << " ";
                     }
         SetConsoleCursorPosition(hOutput, Position1);
         cout << "Are you ready to start playing (y/n)?\n";
         cout << ">>";
         start = getch();
         }
         
         // Clear the question from the window
         for(i=0; i<=WIDTH; i++)
         {
                  for(j=56; j<=59; j++)
                  {
                           Position1.X = i;
                           Position1.Y = j;
                           SetConsoleCursorPosition(hOutput, Position1);
                           cout << " ";
                  }
         }
         
         game_main();
    }
    
    void game_keyboard()
    {
         if( (i-1)>0 ) {
             switch(getch()) {
                             grid[i][j] = 0;
                             case 'a': i--; break;
                             grid[i][j] = 1;
                             }
             }
             else if( (i+1)<WIDTH ) {
                  switch(getch()) {
                                  grid[i][j] = 0;
                                  case 'd': i++; break;
                                  grid[i][j] = 1;
                                  }
                  }
                  else if( (j-1)>0 ) {
                       switch(getch()) {
                                       grid[i][j] = 0;
                                       case 'w': j--; break;
                                       grid[i][j] = 1;
                                       }
                       }
                       else if( (j+1)<HEIGHT ) {
                            switch(getch()) {
                                            grid[i][j] = 0;
                                            case 's': j++; break;
                                            grid[i][j] = 1;
                                            }
                            }
                  
    }
    
    void game_translate()
    {
         
    }
    
    void game_graphics_update()
    {
         for(i=0; i<WIDTH; i++)
         {
                  for(j=0; j<HEIGHT; j++)
                  {
                           if( grid[i][j] == 1) {
                               cout << (unsigned char)219;
                               }
                  }
         }
    }
    
    int game_still_running()
    {
        return 1;
    }
    
    void game_main()
    {
         // The first part of this code is just the beginning count down sequence
         COORD Position;
         RUNNING = 1;
         int time;
         time = 200;    // Defines the time between frames
         
         Position.X = 20;
         Position.Y = 24;
         int i,j;
         
         for(i=3; i>=1; i--)
         {
                  SetConsoleCursorPosition(hOutput, Position);
                  cout << "Starting in...\n\n\t\t\t  " << i;
                  Sleep(1000);
         }
         
         game_grid_clear();
         
         Position.X = 25;
         Position.Y = 24;
         SetConsoleCursorPosition(hOutput, Position);
         cout << "GO!";
         Sleep(1000);
         
         // Wipe the screen for the beginning of the game
         game_grid_clear();
         
         // This is where the real game programming happens
         
         // Initially set the grid array to be zeros. If a grid spot is filled, then
         // 1 will be entered in the array
         
         for(i=0; i<WIDTH; i++)
         {
                  for(j=0; j<HEIGHT; j++)
                  {
                           grid[i][j] = 0;
                  }
         }
         
         // Make a block appear
         grid[24][1] = 1;
         
         // The main game loop
         while(RUNNING == 1)
         {
                       // Draw the block
                       game_draw_block();
                       // Sleep for time
                       Sleep(time);
                       // Consider keyboard input
                       game_keyboard();
                       // Move down by one grid space
                       game_translate();
                       // Update Graphics
                       game_grid_clear();
                       game_graphics_update();
                       // game_view_grid();
                       // Test if still running
                       RUNNING = game_still_running();
                       
                       if(RUNNING == 0) {
                                  game_close();
                                  }
         }
    }
    
    void game_draw_block()
    {
         COORD Position;
         
         for(i=0; i<WIDTH; i++) {
                  for(j=0; j<HEIGHT; j++) {
                           if(grid[i][j] == 1) {
                                         Position.X = i;
                                         Position.Y = j;
                                         SetConsoleCursorPosition(hOutput, Position);
                                         cout << (unsigned char)219;
                                         }
                           }
                  }
    }
    
    void game_view_grid()
    {
    
    }
         
    void game_close()
    {
         
    }
    P.S. Some of the functions in the program are either unfinished, or used only in the development stage (e.g. game_view_grid() function). These will be deleted later.

  2. #2
    Unregistered User Yarin's Avatar
    Join Date
    Jul 2007
    Posts
    2,158
    You should store all of your incoming blocks in a rect of a certain size (4x4 I think is standard), that way 1 rotation function will work on a block of any size, only once the block hits the ground should it become part of the matrix.
    Being such a small rect that needs rotated, I would just have the program manually swap different blocks in the rect to make a rotation effect.

  3. #3
    Unregistered User Yarin's Avatar
    Join Date
    Jul 2007
    Posts
    2,158
    Or like this:
    Code:
    void yTetRotate(BYTE *pbRect) // input should be a 4 x 4 rect
    {
       long x, y;
       BYTE newrect[4*4];
       for(x = 0; x < 4; x++)
       {
          for(y = 0; y < 4; y++)
          {
             newrect[(3 - y) + (x * 4)] = pbRect[x + (y * 4)];
          }
       }
       CopyMemory(pbRect, newrect, 4*4);
       return;
    }

  4. #4
    Registered User Swarvy's Avatar
    Join Date
    Apr 2008
    Location
    United Kingdom
    Posts
    195
    Quote Originally Posted by Yarin View Post
    You should store all of your incoming blocks in a rect of a certain size (4x4 I think is standard), that way 1 rotation function will work on a block of any size, only once the block hits the ground should it become part of the matrix.
    Being such a small rect that needs rotated, I would just have the program manually swap different blocks in the rect to make a rotation effect.
    That isn't a bad idea. The way that I am trying atm, is to have a 2D array which stores the locations of the blocks. So, for example, the block shown below
    ......___
    .__|......|__
    |________|

    in the 'grid' appear as:
    000111000
    111111111

    where each bit represents an array element. Then to move the block, I just move each of the elements one by one, that way the shape retains its structure, and it means there is an easy way to identify where it should hit other blocks.

    Do you think that is a good way of doing it?

    Edit: the full stops in the diagram above is because I had a problem making the shape look like a shape using spaces.

    btw, thanks for the code. That is very useful
    Last edited by Swarvy; 04-02-2008 at 09:25 AM.

  5. #5
    Unregistered User Yarin's Avatar
    Join Date
    Jul 2007
    Posts
    2,158
    Well, that's how I tried doing it when I attempted to make a tetris clone (several years ago, now it would be alot easier for me), but it's difficult to do rotation when the blocks are stored like that.

  6. #6
    Registered User Swarvy's Avatar
    Join Date
    Apr 2008
    Location
    United Kingdom
    Posts
    195
    Quote Originally Posted by Yarin View Post
    Well, that's how I tried doing it when I attempted to make a tetris clone (several years ago, now it would be alot easier for me), but it's difficult to do rotation when the blocks are stored like that.
    That is true. I may use your idea with this 'background grid' i have. That way I can easily test if blocks crash, but rotations are also alot easier.

  7. #7
    Why am I a programmer? shoutatchickens's Avatar
    Join Date
    Mar 2008
    Posts
    45
    When I went after a tetris clone a few years ago I made a 3d array. That way I not only had the 2d grid for the blocks, but I could have the different rotations stored also. I think if I was to redo this today, I would just create the one block and use something like Yarin posted.

    Some on this board have suggested just using a whole seperate image to display when the block rotates. With a simple game of tetris i'm not sure the performance is that big of an issue, but it does present another option.

  8. #8
    Registered User guesst's Avatar
    Join Date
    Feb 2008
    Location
    Lehi, UT
    Posts
    179
    Heh, code examples always win over hypothetical discussions.

    I found this tetris clone on the Programmers Heaven board some years ago, but it's (ab)use of the borland-specific conio library function has prevented me from really using it. However, I was going to write my own in curses.

    Notice how it handles the data storage (remember your binary->hex conversion). The blocks AND their 4 rotations are pre-calculated and stored in memory. Also, each block is a square. In your example above:
    the block shown below
    ......___
    .__|......|__
    |________|

    in the 'grid' appear as:
    000111000
    111111111
    you seem to use 3 blocks of data for a square. Reduce that to:
    Code:
    {{0,1,0},
     {1,1,1}}
    Trust me on this one. Using the suggestions above, you probably want to store it in a too-big array to handle rotations:
    Code:
    { {0,0,0,0},
     {0,1,0,0},
     {1,1,1,0},
     {0,0,0,0}}
    Also, the above program demonstrates a solution to your collision detection problem.

    Now, you can still dynamically rotate the data if you want, it's up to you. Expanding your arrays like I showed you (instead of using hex to store it) gives you the option to read left to right, right to left, up to down or down to up easily, which really is all you have to do for Tetris. No 45 degree rotations here.

    Hmm, I think I just answered my own question.
    Type-ins are back! Visit Cymon's Games at http://www.cymonsgames.com for a new game every week!

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. 2D RPG Online Game Project. 30% Complete. To be released and marketed.
    By drallstars in forum Projects and Job Recruitment
    Replies: 2
    Last Post: 10-28-2006, 12:48 AM
  2. Game Designer vs Game Programmer
    By the dead tree in forum Game Programming
    Replies: 8
    Last Post: 04-28-2005, 09:17 PM
  3. People interested in Joint game making in the future
    By aresashura in forum Game Programming
    Replies: 18
    Last Post: 12-31-2001, 01:36 AM
  4. Problem with a game im making
    By TheGr8one in forum Game Programming
    Replies: 2
    Last Post: 10-19-2001, 07:38 PM
  5. Lets Play Money Making Game
    By ggs in forum A Brief History of Cprogramming.com
    Replies: 1
    Last Post: 09-04-2001, 08:36 PM