I'm trying to make a simple tetris game, but I can't figure out how to make the shapes act as one?

I am trying to have the shapes rotate, and I thing that it would be easier if I could group the shapes and have it as a variable, and then have that rotate. I would also appreciate it if anybody could help me figure out how to stack the blocks. Thanks Heres my code:

float randomShape; float x; float y; float ySpeed; float xSpeed; void setup(){ size(500,1000); frameRate(.5); x=250; y=0; xSpeed=25; ySpeed=25; }

void draw(){ float randomShape = random(0,5); y=y+ySpeed; //movement with keyboard if (key==LEFT){ x=x-5; } if (key==RIGHT){ x=x+5; } //L shape if (randomShape>0 && randomShape<1) { fill(245,170,15); rect(x,y,25,75); rect(x+25,y-50,25,25); } //Z shape if (randomShape>1 && randomShape<2) { fill(100,245,15); rect(x,y,25,50); rect(x+25,y+25,25,50); } //rectangle if (randomShape>2 && randomShape<3) { fill(15,210,245); rect(x,y,100,25); } //square if (randomShape>3 && randomShape<4) { fill(250,250,15); rect(x,y,50,50); } //T shape if (randomShape>4 && randomShape<5) { fill(195,15,250); rect(x,y,75,25); rect(x+25,y+25,25,25); }

}

Tagged:

Answers

  • core idea here would be to represent the pieces by a grid (a two dimensional array) so when displaying the piece you draw the cells of the grid - together with its color

    when you are into oop/object oriented programming you can store shape / grid and color together in the class

    here you can also rotate the grid

    the board is also a much bigger grid, so when a shape lands, copy it into the board.

  • Questions like this make me want to jump up on my desk, pull out my hair, crouch down, and scream "TAKE BABY STEPS!".

    Yes, I get it, the different block shapes are iconic when it comes to Tetris, but they are literally the LAST thing you should try to get working.

    Start simple. Really simple. So simple that nothing is can possibly be wrong. So simple that your sketch doesn't even do anything.

    void setup(){
      size(400,800);
    }
    
    void draw(){
      background(0);
    }
    

    This is already a better sketch that what you've started with up there, because there is NOTHING WRONG with this sketch. As long as we take baby steps and make sure we maintain this "NOTHING IS WRONG" property of our sketch, the end result will also have nothing wrong with it. If we start from your code that's already trying to do Tetris shapes, we're in for a nightmare time of trying to fix issues, because we will have a lot of code and we won't be sure what just changed so the trouble could be in any of the code we have.

  • So a sketch that does nothing is boring. We know we are going to want some blocks, so let's add a grid of blocks.

    void setup() {
      size(400, 800);
    }
    
    void draw() {
      background(0);
      for ( int j = 0; j < 20; j++ ) {
        for ( int i = 0; i < 10; i++ ) {
          rect(30 + 30*i, 30 + 30*j, 30, 30);
        }
      }
    }
    

    Run this. Does it work? Sure. Does it look okay? Not really. It might be nice to center the game field.

    void setup() {
      size(400, 800);
    }
    
    void draw() {
      translate(width/2, height/2);
      background(0);
      for ( int j = 0; j < 20; j++ ) {
        for ( int i = 0; i < 10; i++ ) {
          rect(30 + 30*i, 30 + 30*j, 30, 30);
        }
      }
    }
    

    Run this. Does it work? NO! What changed? Ah, I added a line that tried to center the game field. Since that is the only line that changed, I KNOW EXACTLY WHAT LINE IS THE PROBLEM. This sort of knowledge is CRITICALLY IMPORTANT.

    Knowing which line caused my woes, I can fix it.

    void setup() {
      size(400, 800);
    }
    
    void draw() {
      translate(width/2-150, height/2-300);
      background(0);
      for ( int j = 0; j < 20; j++ ) {
        for ( int i = 0; i < 10; i++ ) {
          rect(30 + 30*i, 30 + 30*j, 30, 30);
        }
      }
    }
    

    There. Now it is centered properlyOH WAIT NO IT'S NOT. Did you try to run it? You have to try to run it every time you make changes to make sure that it works still.

    void setup() {
      size(400, 800);
    }
    
    void draw() {
      translate(width/2-150, height/2-300);
      background(0);
      for ( int j = 0; j < 20; j++ ) {
        for ( int i = 0; i < 10; i++ ) {
          rect(30*i, 30*j, 30, 30);
        }
      }
    }
    
  • edited October 2017

    Of course you really don't want to hard-code values into your code, right? So we can make this a bit more flexible.

    int grid_w = 10;
    int grid_h = 20;
    int grid_s = 30;
    
    void setup() {
      size(360, 660);
      grid_s = min( width/(2+grid_w), height/(2+grid_h) );
    }
    
    void draw() {
      translate((width-(grid_w*grid_s))/2, (height-(grid_h*grid_s))/2);
      background(0);
      for ( int j = 0; j < grid_h; j++ ) {
        for ( int i = 0; i < grid_w; i++ ) {
          rect(grid_s*i, grid_s*j, grid_s, grid_s);
        }
      }
    }
    

    Looking good now. Next, we need some way to know if a space in this grid is filled in with a block or not. So we will need some sort of a data structure to remember this information for every space. A 2D array is perfectly suited for this.

    int grid_w = 10;
    int grid_h = 20;
    int grid_s = 30;
    boolean[][] grid_b = new boolean[grid_h][grid_w]; 
    
    
    void setup() {
      size(360, 660);
      grid_s = min( width/(2+grid_w), height/(2+grid_h) );
    }
    
    void draw() {
      translate((width-(grid_w*grid_s))/2, (height-(grid_h*grid_s))/2);
      background(0);
      noStroke();
      for ( int j = 0; j < grid_h; j++ ) {
        for ( int i = 0; i < grid_w; i++ ) {
          fill(0);
          if ( grid_b[j][i] ) {
            fill(255);
          }
          rect(grid_s*i, grid_s*j, grid_s, grid_s);
        }
      }
    }
    

    Following along? Running this one, and BAM! Your game grid is just gone! Where did it go?

    int grid_w = 10;
    int grid_h = 20;
    int grid_s = 30;
    boolean[][] grid_b = new boolean[grid_h][grid_w]; 
    
    
    void setup() {
      size(360, 660);
      grid_s = min( width/(2+grid_w), height/(2+grid_h) );
    }
    
    void draw() {
      translate((width-(grid_w*grid_s))/2, (height-(grid_h*grid_s))/2);
      background(0);
      stroke(128);
      for ( int j = 0; j < grid_h; j++ ) {
        for ( int i = 0; i < grid_w; i++ ) {
          fill(0);
          if ( grid_b[j][i] ) {
            fill(255);
          }
          rect(grid_s*i, grid_s*j, grid_s, grid_s);
        }
      }
    }
    

    Oh wait, there it is.

  • Of course, it would be nice to see spaces with blocks in them too. Let's set some spaces to be filled by blocks at random:

    int grid_w = 10;
    int grid_h = 20;
    int grid_s = 30;
    boolean[][] grid_b = new boolean[grid_h][grid_w]; 
    
    
    void setup() {
      size(360, 660);
      grid_s = min( width/(2+grid_w), height/(2+grid_h) );
      for ( int j = 0; j < grid_h; j++ ) {
        for ( int i = 0; i < grid_w; i++ ) {
          grid_b[j][i] = random(1) < 0.5;
        }
      }
    }
    
    void draw() {
      translate((width-(grid_w*grid_s))/2, (height-(grid_h*grid_s))/2);
      background(0);
      stroke(128);
      for ( int j = 0; j < grid_h; j++ ) {
        for ( int i = 0; i < grid_w; i++ ) {
          fill(0);
          if ( grid_b[j][i] ) {
            fill(255);
          }
          rect(grid_s*i, grid_s*j, grid_s, grid_s);
        }
      }
    }
    

    So that works.

  • Now, take a tea break. ~O)

    Look at how messy draw() and setup() are getting. Notice that there are many variables that all start with grid_. Think about it for a while.

    Then write a Grid class instead.

    class Grid {
      int w = 10;
      int h = 20;
      int s;
      boolean[][] b = new boolean[h][w]; 
      Grid() {
        s = min( width/(2+w), height/(2+h) );
        randomize();
      }
      void randomize() {
        for ( int j = 0; j < h; j++ ) {
          for ( int i = 0; i < w; i++ ) {
            b[j][i] = random(1) < 0.5;
          }
        }
      }
      void draw() {
        pushMatrix();
        translate((width-(w*s))/2, (height-(h*s))/2);
        background(0);
        stroke(128);
        for ( int j = 0; j < h; j++ ) {
          for ( int i = 0; i < w; i++ ) {
            fill(0);
            if ( b[j][i] ) {
              fill(255);
            }
            rect(s*i, s*j, s, s);
          }
        }
        popMatrix();
      }
    }
    
    // =====
    
    Grid grid;
    
    void setup() {
      size(180, 300);
      grid = new Grid();
    }
    
    void draw() {
      background(0);
      grid.draw();
    }
    

    Gold star if you saw this coming.

  • Of course, we don't just want a grid. We want a grid that knows about some falling blocks. And we want that block to be somewhere in the grid, so we should have a position for the block in the grid and we should tell the grid that we have a block in the grid. And we should have the grid remember that there's a block. And the grid should draw the block it's been told about.

    class Grid {
      int w = 10;
      int h = 20;
      int s;
      int x, y;
      boolean[][] b = new boolean[h][w]; 
      Grid() {
        s = min( width/(2+w), height/(2+h) );
        //randomize();
      }
      void randomize() {
        for ( int j = 0; j < h; j++ ) {
          for ( int i = 0; i < w; i++ ) {
            b[j][i] = random(1) < 0.5;
          }
        }
      }
      void block_at(int ix, int iy){
        x = ix;
        y = iy;
      }
      void draw() {
        pushMatrix();
        translate((width-(w*s))/2, (height-(h*s))/2);
        background(0);
        stroke(128);
        for ( int j = 0; j < h; j++ ) {
          for ( int i = 0; i < w; i++ ) {
            fill(0);
            if ( b[j][i] ) {
              fill(255);
            }
            if( j==y && i == x){
              fill(255,0,0);
            }
            rect(s*i, s*j, s, s);
          }
        }
        popMatrix();
      }
    }
    
    // =====
    
    Grid grid;
    
    int block_x = 3;
    int block_y = 4;
    
    void setup() {
      size(180, 300);
      grid = new Grid();
    }
    
    void draw() {
      background(0);
      grid.block_at(block_x, block_y);
      grid.draw();
    }
    
  • edited October 2017

    Of course the block is a bit more complex than that. It has to fall down after a while, right? And the keys need to move it around?

    class Grid {
      int w = 10;
      int h = 20;
      int s;
      int x, y;
      boolean[][] b = new boolean[h][w]; 
      Grid() {
        s = min( width/(2+w), height/(2+h) );
        //randomize();
      }
      void randomize() {
        for ( int j = 0; j < h; j++ ) {
          for ( int i = 0; i < w; i++ ) {
            b[j][i] = random(1) < 0.5;
          }
        }
      }
      void block_at(int ix, int iy){
        x = ix;
        y = iy;
      }
      void draw() {
        pushMatrix();
        translate((width-(w*s))/2, (height-(h*s))/2);
        background(0);
        stroke(128);
        for ( int j = 0; j < h; j++ ) {
          for ( int i = 0; i < w; i++ ) {
            fill(0);
            if ( b[j][i] ) {
              fill(255);
            }
            if( j==y && i==x ){
              fill(255,0,0);
            }
            rect(s*i, s*j, s, s);
          }
        }
        popMatrix();
      }
    }
    
    // =====
    
    Grid grid;
    
    int block_x = 3;
    int block_y = 4;
    int block_t;
    
    void setup() {
      size(180, 300);
      grid = new Grid();
      block_t = millis() + 1000;
    }
    
    void draw() {
      background(0);
      if( millis() > block_t ){
        block_t = millis() + 1000;
        block_y++;
      }
      grid.block_at(block_x, block_y);
      grid.draw();
    }
    
    void keyPressed(){
      if( keyCode == UP ){ block_up(); }
      if( keyCode == DOWN ){ block_down(); }
      if( keyCode == LEFT ){ block_left(); }
      if( keyCode == RIGHT ){ block_right(); }
    }
    
    void block_up(){
      // TODO: Instant drop.
    }
    
    void block_down(){
      block_t = millis() + 1000;
      block_y++;
    }
    
    void block_right(){
      block_x++;
    }
    
    void block_left(){
      block_x--;
    }
    

    Next up is another tea break. Think about things for a while and see if you can get the gold star this time.

  • class Grid {
      int w = 10;
      int h = 20;
      int s;
      int x, y;
      boolean[][] b = new boolean[h][w]; 
      Grid() {
        s = min( width/(2+w), height/(2+h) );
        //randomize();
      }
      void randomize() {
        for ( int j = 0; j < h; j++ ) {
          for ( int i = 0; i < w; i++ ) {
            b[j][i] = random(1) < 0.5;
          }
        }
      }
      void block_at(int ix, int iy) {
        x = ix;
        y = iy;
      }
      void draw() {
        pushMatrix();
        translate((width-(w*s))/2, (height-(h*s))/2);
        background(0);
        stroke(128);
        for ( int j = 0; j < h; j++ ) {
          for ( int i = 0; i < w; i++ ) {
            fill(0);
            if ( b[j][i] ) {
              fill(255);
            }
            if ( j==y && i==x ) {
              fill(255, 0, 0);
            }
            rect(s*i, s*j, s, s);
          }
        }
        popMatrix();
      }
    }
    
    // =====
    
    class Block {
      int x = 3;
      int y = 4;
      int t;
      Block() {
        t = millis() + 1000;
      }
      void draw() {
        if ( millis() > t ) {
          t = millis() + 1000;
          y++;
        }
        grid.block_at(x, y);
      }
      void up() {
        // TODO: Instant drop.
      }
      void down() {
        t = millis() + 1000;
        y++;
      }
      void right() {
        x++;
      }
      void left() {
        x--;
      }
    }
    
    // =====
    
    Grid grid;
    Block block;
    
    void setup() {
      size(180, 300);
      grid = new Grid();
      block = new Block();
    }
    
    void draw() {
      background(0);
      block.draw();
      grid.draw();
    }
    
    void keyPressed() {
      if ( keyCode == UP ) { 
        block.up();
      }
      if ( keyCode == DOWN ) { 
        block.down();
      }
      if ( keyCode == LEFT ) { 
        block.left();
      }
      if ( keyCode == RIGHT ) { 
        block.right();
      }
    }
    

    ~O)

  • edited October 2017

    Anyway, that is way more than enough help. Plus Chrisir is all mad at me. From here, you should work on what happens to a block when it moves down... Does it always just move down a space? What if the space below is occupied? What happens then? Where does the block go to? What happens if THAT space is occupied??

    Once you have a single block working, try to get four of them going. Can you get them to all stop at once? You might want to make sure that they can ALL move down before ANY of them actually move down. Same thing with left and right movement - make sure they can ALL move left (or right) before they actually do the move.

    Add instant drop. This just means that the blocks fall immediately as much as they can until they can't, then falls again (and that triggers something else to happen).

    Vary the shapes your four blocks make. Perhaps you can store ints in the grid's array instead of booleans, and use that as an index into an array of colors to make things pretty. Make sure to update and checks that look for placed blocks in the grid!

    How can you check for a completed row? When should you check for this? What happens in that case?

    Rotation! Geez! How is that even going to work? Maybe one of the blocks in a formation should be the block the others rotate about? Or can you just change to a different formation?

  • This is an incredible response, @TfGuy44. Bravo

  • This post sequence would make a great tutorial document, @TfGuy44 (and several of your other post sequences, actually).

Sign In or Register to comment.