Wednesday 18 April 2012

Game Pack: Tetris


Now we can get control input to the TouchShield the next step is to use it to control a game. One of my favourite games of all time (up there with Zelda: Majora's Mask, Mario 64 and Secret of Mana) is Tetris. It's just so simple, addictive and satisfying that you can play it all day. This seemed like a realistic first game to be able to complete, so I have made a simple Tetris based on the most popular released version on the Nintendo Game Boy from 1989.

You can download the Eclipse project for Tetris here. You just need to import it into Eclipse.

Note: When testing importing the project into a different instance of Eclipse it built fine in 'Debug' mode but threw an error in 'Release' mode saying something like 'bad address'. Cleaning the project made this error go away and the project built successfully. Also I had to change the AVRDude programmer configuration back to TouchShield as it was set to ArduinoUno and so the program wouldn't upload as the chip was not in sync with the uploader.

The InputShieldForwarder is still used on the Arduino, and this new Tetris project is designed to be uploaded to the TouchShield.

Here is a video of the game in action:



The main considerations I took into account when making the game were:

  1. Providing a good frame rate
  2. Ensuring the game can be played indefinitely (i.e. no memory leaks)
  3. Making the blocks rotate in the proper way
  4. Making the graphics look like the Game Boy version

Providing a good frame rate


If nothing is happening in the game (no graphics changing, no logic processing) the frame rate will be very high as each game loop will execute very quickly. If something quite intensive is happening (like lines being matched on the screen and lots of blocks being shuffled down to fill the gap) the frame rate will be much slower or at least shutter for a few frames.

I found that if the frame rate was too high then the user input would be applied too quickly and blocks would shoot across the screen making fine positioning impossible.


In order to level this out you can throttle the frame rate by only allowing a game loop to execute after a specific period has elapsed. For Tetris I chose a frame rate of 40fps which looks like smooth. This is achieved in Tetris.cpp:


 long curr_time = millis();  
   
   if(millis() - curr_time > (1000/40)){  
   
     //do game processing  
   
     curr_time = millis();  
   }  

Ensuring the game can be played indefinitely


I spent quite a while play-testing the game and am pretty sure there are no memory leaks present. Having come from a mainly Java background I found it quite challenging ensuring each object I created was destroyed again at the appropriate time (having been spoilt with automatic behind-the-scenes garbage collection and a memory space that was unlikely to run out). This was achieved through the use of class destructors and calls to delete() when an object was no longer needed.

The only transient objects in the game are Tetromino and GridBlock. A Tetromino object represents the currently falling block and is used to coordinate the 4 sub-blocks when it moves around the grid. A Tetromino is created when a new block is required to fall from the top of the grid. It is destroyed when it collides with either another block or the bottom of the grid. A GridBlock object represents a single sub-block within a Tetromino or a single occupied square on the grid. Four GridBlocks are created when a Tetromino is created. When a Tetromino is destroyed the GridBlocks remain as they are now part of the grid and can interact with future falling blocks. GridBlocks are destroyed when they are cleared in a line.



Making the blocks rotate in the proper way


Each block can be rotated 90 degrees into 4 positions. These rotations are not necessarily around the central point of the block and the final position of the block is allowed to be shifted left or right to fit into the available space if possible.

For example, s-blocks and line-blocks do not rotate around a single point:

Bottom row shows real rotation behaviour of an s-block



Bottom row shows real rotation behaviour of a line-block
however t-blocks and l-blocks do:

Real rotation behaviour matches rotating around a single point



Real rotation behaviour matches rotating around a single point


This means there is no common rule of rotation. (Or at least not one that I could see).

I decided to implement block rotation as a set of 4 translations for each block. In order for the blocks to match the configuration for a given rotation, the 4 sub-blocks need to be translated into the appropriate positions. This makes rotation really easy as it just involves a relative X/Y coordinate translations for each sub-block.

For example, to rotate the given t-block from the left position to the right position, the following translations need to be applied:


red block += (1,-1)
blue block += (0,0)
green block += (-1,1)
yellow block += (1,1)

You can see these rotation translations in the Tetromino sub-classes in the project.

Also the blocks are allowed to shift to fit into the available space. This makes it easier on the player as they don't have to move the blocks into positions where rotations will fit, the grid will handle that for them.

For example, rotating an s-block from position 2 while it is against a wall would result in one of the sub-blocks ending up inside the wall. However as there is space to shift, the block does so:

block shifting after rotation


The only time a rotation will fail is if there is no amount of shifting that can be done to fit the new configuration on the grid without colliding with another block or the edges of the grid.

shifting right allows the rotation to fit

No amount of shifting will allow the rotation to fit


Making the graphics look like the Game Boy version


I have very fond memories of playing Tetris on my older brother's Game Boy as a child. The yellow hued screen and sepia-toned blocks had to be recreated on the TouchShield for maximum authenticity! I thought it seemed a shame to deliberatly make the graphics look previous-gen as the TouchShield is capable of providing super vibrant, clear images that wouldn't look out of place on any modern hand-held device. However as the look was completed I changed my mind and thought it made the game look awesome. I'm not too keen on the look of cheesy brightly coloured Tetris clones that are floating around, but I feel this retro-style makes it stand out. It could do with some fake screen-bleed at the edges though...



While the finished game is pretty cool, it could do with some improvements that I will work on soon (assuming the Tetris god is smiling on me):

  1. Saving hi-scores
  2. Option to switch out the current falling block with a saved block
  3. Sorting out the level speed scaling
  4. Sound (it could definitely do with some epic tunes)

No comments:

Post a Comment