Skip to content

Appendix A. Collision Detection and PlayField

linkous8 edited this page Jan 8, 2019 · 2 revisions

In this section, we will take time to discuss the collision detection function in detail as well as detecting full rows after a piece locks. To recap, we don't want to move a piece into occupied space so we apply this method to determine if movement is possible.

In game.c, you will find the boolean function doesCollide(). This evaluates the file scoped playField[][] and currentPiece variables and returns true if there is overlap between the current piece, and a wall or piece locked into the playfield.

This function creates a 2 byte bitmask of the playField at the 4x4 position we are attempting to move the currentPiece into using a for loop starting at the current piece's y position minus 2 and terminating at y + 2. You'll see we also have a variable bitMaskDec set to 0x8000 which is the same as 1000000000000000 in binary. Each time after we evaluate a position, we shift that 1 bit right one position (eg. 0100000000000000) such that the higher order bits of the fieldMask represent the upper rows of the 4x4 piece of playfield. Each time we determine the space of the playField is occupied, we set its corresponding bit to 1 with a bitwise OR equals. I'll give a brief rundown of that logic below.

When y is > 19, the space is occupied by the floor and we set all bits for the current row to 1 accordingly. Otherwise, we iterate the 4 blocks of the x axis and check if there is a wall, or if the playField is occupied. (note: y can be negative because part of the piece appears above the playField during initial spawn but we dont actually store those positions in playField. Yes, that means parts of pieces locked above the playField effectively disappear)

Once we have the bitMask of the field, we use a helper function to get a corresponding bitmask for the currentPiece's currentRotation. This is immediately bitwise ANDed with the fieldMask to produce nonzero values when a collision occurs, and a value of zero when the piece does not overlap with a wall, floor, or occupied space.

The PlayField and Checking for full Rows

The playfield of the game is represented by a 10x20 2d array of unsigned 8 bit integers. When a position is empty, its value is zero. Otherwise it is a non-zero ID corresponding to a position in the sprite sheet which we use for drawing the play field.

You can see this in action in the game function during the locking logic as well as the clear logic. In the locking logic, we use the same pieceMasks from the collision detection to determine if the piece occupies a particular space in the playField. A helper method then gets a sprite sheet ID for each block of the current piece being locked and set it's corresponding position in playField to that value.

Next when we check for rows to clear, we loop the 4 rows occupied by the piece that was just locked (much like in collision detection and locking) but this time we iterate the whole width of the x axis checking their values. If all values in a row are non-zero, it is complete and can be cleared.

Clone this wiki locally