Make Your Own Sideways Scroller: Part 6 - Detecting Crashes and Saving Scores


(Simon) #1

The code described in these lessons can be found at https://github.com/filmote/Steve


Detecting Crashes

The Arduboy library provides a simple function that detects the collision between two rectangles. To detect a collision between Steve and a cactus, the rectangular boundary of the images could be supplied.

The following code loops through the array of obstacles and test for collisions between Steve and enabled or active obstacles. The boundaries of each image are captured into a Rect object - a structure provided by the Arduboy library - and these use as parameters in the Arduboy collision() method. If a collision or overlap of the two rectangles is detected, the function returns true.

bool collision () {
    
  for (byte i = 0; i < NUMBER_OF_OBSTACLES; i++) {

    if (obstacles[i].enabled == true) {

      Rect steveRect = Rect{ steve.x, 
                             steve.y - getImageHeight(steve.image),
                             getImageWidth(steve.image),
                             getImageHeight(steve.image) };
      Rect obsRect =   Rect{ obstacles[i].x, 
                             obstacles[i].y - getImageHeight(obstacles[i].image),
                             getImageWidth(obstacles[i].image), 
                             getImageHeight(obstacles[i].image) };

      if (arduboy.collide(steveRect, obsRect)) {

        return true;
          
      }
      
    }
    
  }

  return false;
 
}

Hang on! This code isn’t the same as in the sample code. Right … the reason for this is that the standard collision code works well for detecting head-on collisions between images that fill the majority of the rectangle they are contained within but performs poorly when trying to detect collisions between image corners or images that do not fill the entirety of their containing rectangle.

This is shown in the example below. Although the Arduboy collision() function reports a collision, they are clearly not touching.

Picture1

The code in the sample application uses a modified collision detection function that I have described in detail in the Arduboy Magazine which looks at the pixels of the images themselves rather than the containing rectangle to detect a collision.

If you are interested, you can read this advanced article:

Saving Scores

The Arduboy includes a small amount of non-volatile memory, known as EEPROM, which can store and retain information even when the unit is turned off.

EEPROM is ideal for saving user settings, high scores and other information between sessions. EEPROM stands for Electrically Erasable Programmable Read-Only Memory but this is a misnomer as the memory can actually be updated. EEPROMs have a limited life and will eventually fail after they have been erased and rewritten too many times – this number may be in the millions of operations but a poorly written program that attempts to use it as working memory could easily reach that.

The EEPROM class provides three basic functions to read and write a single byte of memory, as shown below. The memory location can be anywhere in the 1Kb and equates to a value between 0 and 1023. The update() function differs from the write() function in that it checks the value to be written against what is already stored in order to minimize the number of updates thus prolonging the life of the EEPROM.

EEPROM.read(memory_location);
EEPROM.update(memory_location, value);
EEPROM.write(memory_location, value);

The library also offers two other functions that can save and retrieve datatypes other than a byte, such as a float, integer or even a structure.

EEPROM.put(memory_location, value);
EEPROM.get(memory_location, value);

Using these functions, we can save Steve’s top scores. We can save it anywhere in the 1Kb range however the first 16 bytes are reserved for storing Arduboy system details including the current sound state (on / off), the unit name and other bits and pieces.

The Arduboy library defines a constant, EEPROM_STORAGE_SPACE_START, which indicates the first memory location free for user information. The code below allows us to save and retrieve Steve’s score into the lowest available EEPROM memory location.

EEPROM.get(EEPROM_STORAGE_SPACE_START, highScore);
EEPROM.put(EEPROM_STORAGE_SPACE_START, highScore);

Depending on what other games we have been playing previously, these memory locations may contain invalid data that can cause an error or, at worst, report unrealistically high scores. To overcome this, I like to store two fixed characters in front of my application’s data. When the application starts, it checks in the EEPROM memory for the two characters and if it does not find them clears out the memory it plans to use. It then populates the two characters so future checks do not clear the score again.

This is achieved using the following code:

#define EEPROM_START_C1                 EEPROM_STORAGE_SPACE_START
#define EEPROM_START_C2                 EEPROM_START_C1 + 1
#define EEPROM_SCORE                    EEPROM_START_C1 + 2


void initEEPROM() {

  unsigned char c1 = EEPROM.read(EEPROM_START_C1);
  unsigned char c2 = EEPROM.read(EEPROM_START_C2);

  if (c1 != ‘S’ || c2 != ‘T’) {   
  
    EEPROM.update(EEPROM_START_C1, ‘S’);
    EEPROM.update(EEPROM_START_C2, ‘T’);
    EEPROM.put(EEPROM_SCORE, (unsigned int)0);
      
  }

}

The code described in these lessons can be found at https://github.com/filmote/Steve

Prev Article > Make Your Own Sideways Scroller: Part 5 - Launching and Moving Obstacles

Next Article > Make Your Own Sideways Scroller: Part 7 - Putting it all Together


Make Your Own Sideways Scroller: Part 7 - Putting it all Together