Implementation of keyPressed and a special endstate for Tetris clone?

edited October 2015 in Questions about Code

Hey guys, I've been working on a simple Tetris clone with the help of my teacher for an art assignment and since he is unable to help at the moment, I thought it might be best to turn to a forum which could help! I've got the basic layout but I'm new to Processing and coding in general and so can't figure out how to properly implement the keyPressed function to move the current falling object left/right, and I also want to pause the reset function that occurs when the blocks reach the top, so there's some string that says 'Your completed object' or something along those lines (I can edit later obviously).

My Tetris clone doesn't wipe lines of blocks, as it's all about the completed object at the end. I just need help with the implementation of these two things, can anyone help me out, pretty please? :) Code is below, apologies, can't figure out 'code' button:

  int griDim = 40; //grid dimension
  boolean trigger = false; // triggers the trigger() function to create a new block
  int count = 1; // count of number of blocks

  tetshap [] tetShaps = new tetshap[1000]; //creates an array of objects called tetShaps of class tetshap

  void setup() 
  {
    size(600, 600);
    frameRate(40);
    tetShaps[0] = new tetshap();  //create a first shape

  }

  void draw() { 
    background(255); //always draw a new background

    for (int x=0; x < tetShaps.length; x++){ //go through all shapes in the array
    if (tetShaps[x] != null){ // check to make sure there is a shape at this address
    tetShaps[x].drawTetShap();  //for the object in tetShaps[x] call the function drawTetShap()
    tetShaps[x].checkposition();}
    if (tetShaps[x] != null){  //needed to check for null again here in order to cope with refreshed state
    tetShaps[x].update(); //for the object in tetShaps[x] call the function update()
    }
    }

  } 

  void trigger(){ // this function is called once a collision is detected

    println(count); // just keeping track of the number of objects
    tetShaps[count] = new tetshap(); //make a new shape 
    count++; //add to the count of blocks

  }

  void emptyArray(){ //this function clears the array when a block reaches the top
   for(int x=0; x < tetShaps.length; x++){ // go through the array and set all to null (erasing all shapes)
    tetShaps[x] = null; 
   }
   tetShaps[0] = new tetshap();  //create a new shape to start again
   count=1; // reset count
   return; //return a way of breaking out of the existing loo

  }

  class tetshap { 



    int[][] tetShapArr = new int[4][3];  
    int speed;
    color col = color((int)random(255),(int)random(255),(int)random(255));
    boolean status = true;
    int side = width/(griDim);



    tetshap (){ //constriuctor that is called to make a new shape


     speed = 1; //this is one to keep collision simple for the moment
     int ypos = -10;
     int xpos = (int)random(griDim+1);
     for(int x=0; x< tetShapArr.length; x++){ //creating the tetris object

       tetShapArr[x][0] = xpos;
       tetShapArr[x][1] = ypos;
       tetShapArr[x][2] = (int)random(4);
       if (tetShapArr[x][2] == 0){
         ypos--;}
       if (tetShapArr[x][2]==1){
         xpos++;}
       if (tetShapArr[x][2]==2){
         ypos++;}
       if (tetShapArr[x][2]==3){
         ypos++;}
     }

    }

    void drawTetShap() { //draws all the shapes in the array every frame

      fill(col); 

      for(int x=0; x< tetShapArr.length; x++){ 

        int xDim = tetShapArr[x][0]*(width/(griDim+1));
        int yDim = tetShapArr[x][1]*(height/(griDim+1));
        side = width/(griDim+1);
        rect(xDim,yDim,side,side);


      }
    }

    void update() {

        if (status==true){
        for(int x=0; x< tetShapArr.length; x++){

           tetShapArr[x][1]++;

        }
        }

    }

    void checkposition() {


     if (status==true){   // this section checks for collisions if there was a collision it calls triiger to create a new shape
      for(int i=0; i < tetShapArr.length; i++){
      for(int x=0;x < count-1;x++){  
        for(int y=0; y < tetShaps[x].tetShapArr.length; y++){
          if (tetShaps[x].tetShapArr[y][0] == tetShapArr[i][0]
              &
              tetShaps[x].tetShapArr[y][1] == tetShapArr[i][1]+1


              ){
            status = false;
            trigger();
            return;

          }

        } 
      }
      }
     } 

      if (status==true){
      for(int x=0; x < tetShapArr.length; x++){

           if (tetShapArr[x][1]*(height/(griDim+1)) >= height-side){

             status = false;

             trigger();
             return;

           } 
        } 
      } else { // this section check for the object reaching the top and calls 
       if (status==false){ 
       for(int x=0; x < tetShapArr.length; x++){
        if (tetShapArr[x][1]*(height/(griDim+1)) <= 0){

          emptyArray();
          return;

       }



  }
  }
      }
    }}
Tagged:

Answers

  • Please proper format the code.

    http://forum.processing.org/two/discussion/8045/how-to-format-code-and-text#latest

    in short: a skipped line before and after, select it, hit ctr o

  • Thanks _vk, ended up just highlighting it all and ctr + o. Is this the right place to post my question by the way?

  • Any help guys?

  • I did not really understand the second question, sorry. But for handling the key-events, you could do it similar to this:

    void keyPressed() {
      // do something when key is left or right arrow
      if (keyCode == LEFT || keyCode== RIGHT) {
        // get the array that stores the positions
        int[][] tetShapArr = tetShaps[count-1].tetShapArr;
    
        // change the x-position for every element
        for (int i=0; i< tetShapArr.length; i++) {
          if (keyCode == LEFT)       
            tetShapArr[i][0]--;
          else
            tetShapArr[i][0]++;
        }
      }
    }
    

    This moves them only once per key-press, if you want them to move as long as you press the key, then you could change your update-function to something like this:

    void update() {
        if (status==true) {
          for (int x=0; x< tetShapArr.length; x++) {
            tetShapArr[x][1]++;
    
            if (keyPressed && keyCode == LEFT)       
              tetShapArr[x][0]--;
            else if (keyPressed && keyCode == RIGHT)
              tetShapArr[x][0]++;
          }
        }
      }
    

    These examples do not check if the tiles leave the screen, but maybe it's a start.

  • edited October 2015

    Thank you for your contribution benja, appreciate it. I understand that function now, thanks :) . In regards to my second point, I was wondering how I would go about pausing this section: 'emptyArray(); return;' So instead of immediately refreshing the game screen, it could display some text on top of the screen on a 5 second timer or something? Any thoughts on that?

  • Instead of calling "emptyArray" directly, you could set a boolean, indicating, that the game is over. In draw() you could then decide what shall be done when the game is over. Here is a small example that should show the concept:

    int count =0;
    boolean gameOver =false;
    long theTime;
    
    void setup() {
      size(200, 200);
      fill(0);
    }
    
    void draw() {
      // game is running
      if (!gameOver) {
        background(count);
        if (count >=255) {
          // change game-state
          gameOver =true;  
          // remember time
          theTime = millis();
        }
      } 
      // game is over
      else {
        background(200);
        text("game over", 20, 100);
        // when 5 seconds have passed, restart the game
        if (millis()-theTime > 5000) {
          restart();
        }
      }
      count++;
    }
    
    void restart() {
      gameOver =false;
      count =0;
    }
    
  • Hey benja, thanks again. I'm having more trouble implementing this; not sure where to make these changes without destabilizing the rest of the code. Did you manage to get it working?

  • Yes, i could post a working code, but it was better if we could lead you to solve it yourself.

    So as a start:

    Create variables to store the state of the game and the time when the game ends:

    boolean gameOver = false;
    long endTime;
    

    At the point where you have resetArray() at the moment, you can write this instead:

    gameOver = true;
    endTime = millis();
    

    Then in draw() you can do things depending on the state of "gameOver"

    if(!gameOver)
      tetShaps[x].checkposition();
    

    And important, when gameOver is true, then you have to check if enoght time has passed and if that is the case, you can reset the game:

          if (gameOver) {
            text("game over", 20, 100);
            if (millis()-endTime > 5000) {
              gameOver = false;      
              emptyArray();
            }
          }
    

    Please try if you get it working, and post your code, if you have problems.

  • Thanks, that extra explanation helped a lot and I was able to work it in a lot easier than before :) The 'emptyArray();' function still doesn't seem to be working, it says 'game over' but doesn't clear the screen/reset after 5 seconds. Here's my code, perhaps I falsely installed those parts you suggested:

    int griDim = 40; //grid dimension
      boolean trigger = false; // triggers the trigger() function to create a new block
      int count = 1; // count of number of blocks
      boolean gameOver = false;
      long endTime;
      tetshap [] tetShaps = new tetshap[1000]; //creates an array of objects called tetShaps of class tetshap
    
      void setup() 
      {
        size(600, 600);
        frameRate(20);
        tetShaps[0] = new tetshap();  //create a first shape
    
      }
    
      void keyPressed() {
      if (keyCode == LEFT || keyCode== RIGHT) {
        // get the array that stores the positions
        int[][] tetShapArr = tetShaps[count-1].tetShapArr;
    
        // change the x-position for every element
        for (int i=0; i< tetShapArr.length; i++) {
          if (keyCode == LEFT)       
            tetShapArr[i][0]--; //this moves the shape left ('--' on x-axis)
          else
            tetShapArr[i][0]++; //this moves the shape right ('++' on x-axis)
        }
      }
    }
    
      void draw() { 
        background(255); //always draw a new background
    
        for (int x=0; x < tetShaps.length; x++){ //go through all shapes in the array
        if (tetShaps[x] != null){ // check to make sure there is a shape at this address
        tetShaps[x].drawTetShap();  //for the object in tetShaps[x] call the function drawTetShap()
        tetShaps[x].checkposition();}
        if (tetShaps[x] != null){  //needed to check for null again here in order to cope with refreshed state
        tetShaps[x].update(); //for the object in tetShaps[x] call the function update()
        if(!gameOver){
        tetShaps[x].checkposition();}
        if (gameOver) {
      text("game over", 20, 100);
      if (millis()-endTime > 500) {
        gameOver = false;      
        emptyArray();
      }
    }
        }
        }
    
      } 
    
      void trigger(){ // this function is called once a collision is detected
    
        println(count); // just keeping track of the number of objects
        tetShaps[count] = new tetshap(); //make a new shape 
        count++; //add to the count of blocks
    
      }
    
      void emptyArray(){ //this function clears the array when a block reaches the top
       for(int x=0; x < tetShaps.length; x++){ // go through the array and set all to null (erasing all shapes)
        tetShaps[x] = null; 
       }
       tetShaps[0] = new tetshap();  //create a new shape to start again
       count=1; // reset count
       return; //return a way of breaking out of the existing loo
    
      }
    
      class tetshap { 
    
    
    
        int[][] tetShapArr = new int[4][3];  
        int speed;
        color col = color((int)random(255),(int)random(255),(int)random(255));
        boolean status = true;
        int side = width/(griDim);
    
    
    
        tetshap (){ //constriuctor that is called to make a new shape
    
    
         speed = 1; //this is one to keep collision simple for the moment
         int ypos = -10;
         int xpos = (int)random(griDim+1);
         for(int x=0; x< tetShapArr.length; x++){ //creating the tetris object
    
           tetShapArr[x][0] = xpos;
           tetShapArr[x][1] = ypos;
           tetShapArr[x][2] = (int)random(4);
           if (tetShapArr[x][2] == 0){
             ypos--;}
           if (tetShapArr[x][2]==1){
             xpos++;}
           if (tetShapArr[x][2]==2){
             ypos++;}
           if (tetShapArr[x][2]==3){
             ypos++;}
         }
    
        }
    
        void drawTetShap() { //draws all the shapes in the array every frame
    
          fill(col); 
    
          for(int x=0; x< tetShapArr.length; x++){ 
    
            int xDim = tetShapArr[x][0]*(width/(griDim+1));
            int yDim = tetShapArr[x][1]*(height/(griDim+1));
            side = width/(griDim+1);
            rect(xDim,yDim,side,side);
    
    
          }
        }
    
        void update() {
        if (status==true) {
          for (int x=0; x< tetShapArr.length; x++) {
            tetShapArr[x][1]++;
    
            if (keyPressed && keyCode == LEFT)       
              tetShapArr[x][0]--;
            else if (keyPressed && keyCode == RIGHT)
              tetShapArr[x][0]++;
          }
        }
      }
    
        void checkposition() {
    
    
         if (status==true){   // this section checks for collisions if there was a collision it calls triiger to create a new shape
          for(int i=0; i < tetShapArr.length; i++){
          for(int x=0;x < count-1;x++){  
            for(int y=0; y < tetShaps[x].tetShapArr.length; y++){
              if (tetShaps[x].tetShapArr[y][0] == tetShapArr[i][0]
                  &
                  tetShaps[x].tetShapArr[y][1] == tetShapArr[i][1]+1
    
    
                  ){
                status = false;
                trigger();
                return;
    
              }
    
            } 
          }
          }
         } 
    
          if (status==true){
          for(int x=0; x < tetShapArr.length; x++){
    
               if (tetShapArr[x][1]*(height/(griDim+1)) >= height-side){
    
                 status = false;
    
                 trigger();
                 return;
    
               } 
            } 
          } else { // this section check for the object reaching the top and calls 
           if (status==false){ 
           for(int x=0; x < tetShapArr.length; x++){
            if (tetShapArr[x][1]*(height/(griDim+1)) <= 0){
    
              gameOver = true;
              endTime = millis();
    
           }
    
    
    
      }
      }
          }
        }}
    
  • edited October 2015

    You are almost there.

    First advice is to use CTRL-T ot CMD+T to auto-indent your code in processing. That helps to see where functions and conditional blocks end.

    Then you would want to make update() and checkPosition() only be called when "gameOver" is false. You can try this:

    void draw() { 
      background(255);
    
      // this block shall only be executed when game is over
      if (gameOver) {
        text("game over", 20, 100);
        if (millis()-endTime > 5000) {
          gameOver = false;      
          emptyArray();
        }
      }
    
      // the following code can be always executed 
      for (int x=0; x < tetShaps.length; x++) { 
        if (tetShaps[x] != null) { 
          tetShaps[x].drawTetShap();  
    
          // to prevent reseting "endTime" when game is over,
          // we only want to call "checkPosition()" when "gameOver" is false
          if (!gameOver) {
            tetShaps[x].checkposition();
          }
        }
        if (tetShaps[x] != null) {  
          // we only want to update objects while game is running
          if (!gameOver) {
            tetShaps[x].update();
          }
        }
      }
    
    } 
    
  • It's telling me 'Duplicate local variable: x' in the for (int x=0; x < tetShaps.length; x++) { line. I know the problem but not sure how to fix it... Code below, (sorry for poor formatting):

    void draw() { 
      background(255); //always draw a new background
    
      for (int x=0; x < tetShaps.length; x++) { //go through all shapes in the array
        if (tetShaps[x] != null) { // check to make sure there is a shape at this address
          tetShaps[x].drawTetShap();  //for the object in tetShaps[x] call the function drawTetShap()
          tetShaps[x].checkposition();
        }
        if (tetShaps[x] != null) {  //needed to check for null again here in order to cope with refreshed state
          tetShaps[x].update(); //for the object in tetShaps[x] call the function update()
          if (gameOver) {
            text("game over", 20, 100);
            if (millis()-endTime > 5000) {
              gameOver = false;      
              emptyArray();
              for (int x=0; x < tetShaps.length; x++) { 
                if (tetShaps[x] != null) { 
                  tetShaps[x].drawTetShap();  
                  if (!gameOver) {
                    tetShaps[x].checkposition();
                    if (tetShaps[x] != null) {  
                      if (!gameOver) {
                        tetShaps[x].update();
                      }
                    }
                  }
                }
              }
            }
          }
        }
        if (!gameOver) {
          tetShaps[x].checkposition();
        }
        if (gameOver) {
          text("game over", 20, 100);
          if (millis()-endTime > 500) {
            gameOver = false;      
            emptyArray();
          }
        }
      }
    }
    
  • It's because you are nesting for-loops and declare the same variable "x" in both of them.
    Actually, i have the impression that you just mixed my code with yours without understanding what it does. You don't need more than one loop there. Look at your original code and compare it to mine. What has changed? Do you understand why? What parts of the code are executed when gameOver is true or false?

  • Yes, I admittedly shoehorned your last contribution in without giving it as much thought as I should have; sorry, it's been a long day and I'm finding it hard to concentrate properly. Nevertheless, I went back through it and put in the 'if gameOver' clauses but it still doesn't display the ''game over'' text at the end, nor does it emptyArray. Probably some basic oversight on my part but I can't quite pin it down, sorry to have to ask:

    void draw() { 
      background(255); 
    
      for (int x=0; x < tetShaps.length; x++) { 
        if (tetShaps[x] != null) { 
          tetShaps[x].drawTetShap();  
          if (!gameOver) {
            tetShaps[x].checkposition();
          }
          if (tetShaps[x] != null) {  
            if (!gameOver) {
              tetShaps[x].update(); 
              if (!gameOver) {
                tetShaps[x].checkposition();
              }
              if (gameOver) {
                text("game over", 20, 100);
                if (millis()-endTime > 500) {
                  gameOver = false;      
                  emptyArray();
                }
              }
            }
          }
        }
      }
    }
    
  • It's how you put your if-clauses one into another. And you have this part double:

    if (!gameOver) {
                tetShaps[x].checkposition();
    

    What about my prior code? Does it work if you just replace your draw()-function with mine?

  • Okay thanks for pointing that out, not sure how I missed that. Seems to be working well now, thanks benja! Sorry for being a tough case haha.

    Another question if you're willing; would you know how to create (effectively) a barrier in the 'grid' which would prevent the objects in the array from being able to move past the left and right sides of the screen? I'd imagine it would be part of the drawTetShap() function?:

    void drawTetShap() { //draws all the shapes in the array every frame
    
        fill(col); 
    
        for (int x=0; x< tetShapArr.length; x++) { 
    
          int xDim = tetShapArr[x][0]*(width/(griDim+1));
          int yDim = tetShapArr[x][1]*(height/(griDim+1));
          side = width/(griDim+1);
          rect(xDim, yDim, side, side);
        }
      }
    
  • No, you have to check for borders when you change the x-position for your tiles. And only change the x-position when the new position is on screen.
    I just saw, that you included both of the versions for key-handling that i posted. You should only use one of them ( in update() OR in keyPressed() ).

    The way that you create and store your tiles makes the check a little difficult, but you could i.e. just check if tetShapArr[0][0] is between 0 and griDim and only change the x-positions if that is true.

    If that works, you could do the next step and find the lowest/highest x-position in your tile and check against that.

Sign In or Register to comment.