In the previously tutorial, we learn how create the Walls and their gates of the WALLED game.

Today we talk about the collisions between the man and the walls and how to make the score of the game.

Step 1: Start, End and Reset the Game

Before checking the collision between man and walls, we have to prepare the functions and the variables to allow the start and the end of the game. After the End we need to reset the game variables and so we need to create also this function.

At the top of the sketch, after the walls variable and before the ISR(TIM1_OVF_vect) function, insert the gameStarted variable that indicates if the game is started.

...
byte wallGateXPosition = random(0, MATRIX_COL); //randomized wall's gate 
//Game variables 
bool gameStarted = false; //indicates if the game is started 
ISR(TIM1_OVF_vect) {  // timer1 overflow interrupt service routine 
...

Now, we need to modify the first part of the ISR(TIM1_OVF_vect) function in this way:

ISR(TIM1_OVF_vect) {  // timer1 overflow interrupt service routine
  cli(); //disable interrupt
  TCNT1 = 65406;
  //THIS PART IS USED TO UPDATE THE WALL POSITION
  //if game is started change wall position
  if (gameStarted) {
    //it is a counter used to update the wall position after it reach 
    //the wallUpdatePositionSpeed value because timer interrupt is to fast
    wallUpdatePositionCounter++;
    if (wallUpdatePositionCounter > wallUpdatePositionSpeed) {
      updateWallPosition();
      wallUpdatePositionCounter = 0;
    }
  }
...

We have added the check of the variable gameStarted: if this variable is true, so the game is started, we increase the wallUpdatePositionCounter in order to allow the wall’s fall. If this variable is false, it means that the game is ended and the wall stops it fall.

Now we set the gameStarted variable to true at the end of the setup() function:

...
  gameStarted = true; //Start the game
}

After that, we can create this two functions after the loop() function:

void game() {
  updateManPosition(); //update the man position by checking buttons
}
void endGame() {
  clearMatrix(); //clear all the LEDs
  delay(500);
  goSleep(); //sleep to reduce power
  resetGame(); //reset game variables
}

The game() function only calls the updateManPosition() function.

The endGame() function clears the matrix, calls the goSleep() function and then it resets the game with the resetGame() function that we are going to write:

void resetGame() {
  //reset all game variables to the start condition
  clearMatrix();
  delay(300);
  manXPosition = 2;
  wallGateXPosition = random(0, MATRIX_COL);
  wallYPosition = 0; //top screen
  wallUpdatePositionCounter = 0;
  wallUpdatePositionSpeed = wallSpeed;
  //show wall
  for (byte x = 0; x < MATRIX_COL; x++) {
    if (x != wallGateXPosition) {
      matrixState[wallYPosition][x] = 1;
    }
  }
  gameStarted = true;
}

This function resets all the game variables to its original states and puts the gameStarted to true again.

After that we need to modify the loop() function in this way:

void loop() {
  if (gameStarted) { //if game is started
    game(); //go to game function
  } else { //else end the game
    endGame(); //to to end game
  }
}

So, every loop we check if the game is stared. If it is, we call the game() function. Otherwise we call the endGame() function.

Here we are.

If you upload the sketch now, you will not notice anything new than the previous tutorial sketch.

This because our game never ends.

How to end this game? By adding the collision between the man and the wall.

Every time we have a collision our game ends.

Step 2: Check Collisions

To detect a collision between our man and the wall that falls down, we need to check the wall‘s Y position, the man‘s X position and the gate‘s X position.

If the wall‘s Y position is the same of the man‘s Y position – so the bottom line – and the man isn’t in the same X position of the gates, we have a collision and the game ends.

Otherwise if the wall has the same Y position of the man’s and the man’s X position is the same of the gate’s X position, we increase the score.

To check these we only need to add this code at the top of the updateWallPosition() function:

void updateWallPosition() {
  //checkCollision
  if (wallYPosition == MATRIX_ROW - 2) { //if wall is in the same man's Y position
    //NOTE: we check if the wallYposition is in the position MATRIX_ROW - 2 and
    // not on MATRIX_ROW - 1 because we increment the wallYPosition after this check
    if (manXPosition == wallGateXPosition) { //if man passes the wall
      //increase score
    } else {
      gameStarted = false; //esle we end the game
    }
  }
...
NOTE: we check if the wallYPosition is egual to MATRIX_ROW - 2 and not MATRIX_ROW - 1 because we increment the wallYPosition after this check and so we need to check the collision on the previous ROW.

Now, if you upload the sketch, you can notice that, if the wall touch the man where there isn’t a gate, the game stops.

If you want to start the game again, you need to press the two button at the same time.

Now it’s time to add the score!

Step 3: Score

To increase the game score we need to increase a variable – called score – when a man goes through the gate.

First, we need to declare the score variable at the top of the sketch after the declaration of the gameStarted variable.

//Game variables
bool gameStarted = false; //indicates if the game is started
byte score = 0; //game score MAX 255 for byte

Now we can edit the previous checkCollision code in the updateWallPosition() function in this way:

  //checkCollision
  if (wallYPosition == MATRIX_ROW - 2) { //if wall is in the same man's Y position
    //NOTE: we check if the wallYposition is in the position MATRIX_ROW - 2 and
    // not on MATRIX_ROW - 1 because we increment the wallYPosition after this check
    if (manXPosition == wallGateXPosition) { //if man passes the wall
      score++; //increase score
    } else {
      gameStarted = false; //esle we end the game
    }
  }

Every time the man goes through the wall’s gate, we increase the score.

After that, when we have a collision and so the game ends, we need to show the score. To do that, first we need to create the matrix of the numbers.

At the top of the sketch, after the declaration of the score variable, add:

//NUMBERS used in score stored in FLASH in order to reduce RAM size
const PROGMEM bool one[MATRIX_ROW][MATRIX_COL]  = {
  {0, 0, 0, 1, 0, 0},
  {0, 0, 1, 1, 0, 0},
  {0, 0, 0, 1, 0, 0},
  {0, 0, 0, 1, 0, 0},
  {0, 0, 0, 1, 0, 0}
};
const PROGMEM bool two[MATRIX_ROW][MATRIX_COL] = {
  {0, 0, 1, 1, 0, 0},
  {0, 0, 0, 0, 1, 0},
  {0, 0, 0, 1, 0, 0},
  {0, 0, 1, 0, 0, 0},
  {0, 0, 1, 1, 1, 0}
};
const PROGMEM bool three[MATRIX_ROW][MATRIX_COL] = {
  {0, 0, 1, 1, 0, 0},
  {0, 0, 0, 0, 1, 0},
  {0, 0, 0, 1, 0, 0},
  {0, 0, 0, 0, 1, 0},
  {0, 0, 1, 1, 0, 0}
};
const PROGMEM bool four[MATRIX_ROW][MATRIX_COL] = {
  {0, 0, 0, 0, 1, 0},
  {0, 0, 0, 1, 1, 0},
  {0, 0, 1, 0, 1, 0},
  {0, 1, 1, 1, 1, 1},
  {0, 0, 0, 0, 1, 0}
};
const PROGMEM bool five[MATRIX_ROW][MATRIX_COL] = {
  {0, 0, 1, 1, 1, 0},
  {0, 0, 1, 0, 0, 0},
  {0, 0, 1, 1, 0, 0},
  {0, 0, 0, 0, 1, 0},
  {0, 0, 1, 1, 0, 0}
};
const PROGMEM bool six[MATRIX_ROW][MATRIX_COL] = {
  {0, 0, 0, 1, 1, 0},
  {0, 0, 1, 0, 0, 0},
  {0, 0, 1, 1, 1, 0},
  {0, 0, 1, 0, 1, 0},
  {0, 0, 1, 1, 1, 0}
};
const PROGMEM bool seven[MATRIX_ROW][MATRIX_COL] = {
  {0, 1, 1, 1, 1, 0},
  {0, 0, 0, 0, 1, 0},
  {0, 0, 0, 1, 0, 0},
  {0, 0, 1, 0, 0, 0},
  {0, 1, 0, 0, 0, 0}
};
const PROGMEM bool eight[MATRIX_ROW][MATRIX_COL] = {
  {0, 0, 1, 1, 0, 0},
  {0, 1, 0, 0, 1, 0},
  {0, 0, 1, 1, 0, 0},
  {0, 1, 0, 0, 1, 0},
  {0, 0, 1, 1, 0, 0}
};
const PROGMEM bool nine[MATRIX_ROW][MATRIX_COL] = {
  {0, 0, 1, 1, 0, 0},
  {0, 1, 0, 0, 1, 0},
  {0, 0, 1, 1, 1, 0},
  {0, 0, 0, 1, 0, 0},
  {0, 0, 1, 0, 0, 0}
};
const PROGMEM bool zero[MATRIX_ROW][MATRIX_COL] = {
  {0, 0, 1, 1, 0, 0},
  {0, 1, 0, 0, 1, 0},
  {0, 1, 0, 0, 1, 0},
  {0, 1, 0, 0, 1, 0},
  {0, 0, 1, 1, 0, 0}
};

These matrixes are stored in the flash memory to reduce the microcontroller’s RAM usage. We can use this trick because these matrixes are constant.

After that, we can write the following functions after the resetGame() function:

void showScore(byte scoreNumber) {
  clearMatrix();
  char scoreChar[5]; //char were to put the score number
  //converting the score to scoreChar
  String str = String(scoreNumber) + ' ';
  str.toCharArray(scoreChar, 5);
  for (char c = 0; scoreChar[c] != '\0'; c++) {
    for (int col = MATRIX_COL - 1; col >= 0; col--) { // we start to display the charter matrix from right to left
      for (byte i = 0; i < MATRIX_COL; i++) { //put the charter into the matrixState
        for (byte j = 0; j < MATRIX_ROW; j++) { //as usual
          if (i >= col) { //if the number of col(i) is higher than the scrolling col, we show the correct charter according to charterToShow var.
            writeCharter(scoreChar[c], i, j, col);
          } else { //else, if col (i) is less than col, we shift the matrixState
            matrixState[j][i] = matrixState[j][i + 1];
          }
        }
      }
      delay(150);
    }
  }
}
//show the number according to the matrix number
void writeCharter(char charterToShow, byte i, byte j, byte col) {
  if (charterToShow == '0') {
    matrixState[j][i] = (bool*)pgm_read_byte(&(zero[j][i - col]));
  }
  else if (charterToShow == '1') {
    matrixState[j][i] = (bool*)pgm_read_byte(&(one[j][i - col]));
  }
  else if (charterToShow == '2') {
    matrixState[j][i] = (bool*)pgm_read_byte(&(two[j][i - col]));
  }
  else if (charterToShow == '3') {
    matrixState[j][i] = (bool*)pgm_read_byte(&(three[j][i - col]));
  }
  else if (charterToShow == '4') {
    matrixState[j][i] = (bool*)pgm_read_byte(&(four[j][i - col]));
  }
  else if (charterToShow == '5') {
    matrixState[j][i] = (bool*)pgm_read_byte(&(five[j][i - col]));
  }
  else if (charterToShow == '6') {
    matrixState[j][i] = (bool*)pgm_read_byte(&(six[j][i - col]));
  }
  else if (charterToShow == '7') {
    matrixState[j][i] = (bool*)pgm_read_byte(&(seven[j][i - col]));
  }
  else if (charterToShow == '8') {
    matrixState[j][i] = (bool*)pgm_read_byte(&(eight[j][i - col]));
  }
  else if (charterToShow == '9') {
    matrixState[j][i] = (bool*)pgm_read_byte(&(nine[j][i - col]));
  }
  else if (charterToShow == ' ') { //SYMBOLS FOR SPACE
    matrixState[j][i] = 0;
  }
}

The first function – showScore(byte scoreNumber) – is used to show the number in scrolling mode.

When the game ends, we pass the score variable to this function. After, the function clears the matrixState.

Then it converts the score into a char variable.

After, a for cycle is used to scroll the numbers until the scoreChar variable reaches the “end of line charter - \0 “.

The function showScore() takes the number to show from the writeCharter() function that changes the matrixState according to the number – charterToShow – passed to it.

Now we can call the showScore() function in the endGame() function, by edit it in this way:

void endGame() {
  showScore(score); //shows the score number
  clearMatrix(); //clear all the LEDs
  delay(500);
  goSleep(); //sleep to reduce power
  resetGame(); //reset game variables
}

Next we need to clear the score in the resetGame() function. Add score = 0; before gameStarted = true;

...
  score = 0;
  gameStarted = true;
}

You can now upload the sketch to KeyChainino to see the score.

You can download the complete sketch for this third tutorial here.

It’s all for today!

In the next tutorial we add some aesthetical parts and we increase the speed of the walls after a number of walls spawned.

Stay Tuned!

{"cart_token":"","hash":"","cart_data":""}