Game Development of .Nettrix: GDI+ and Collision Detection - The Rotate Method
(Page 16 of 22 )
Although in the previously discussed methods all you needed to do was to change a single coordinate for all the squares of the block (incrementing y to go down, and modifying x to go right or left), in this case you need to change the squares’ positions, one by one, to achieve the effect of rotation. The rotation movement must be based on the block type and on the current orientation of the block.
To track the current rotation applied to the block, you need a new property. Creating a new enumeration for the possible rotation status will make your code more readable.
public enum RotationDirections {
NORTH = 1,
EAST = 2,
SOUTH = 3,
WEST = 4
};
public RotationDirections StatusRotation =
RotationDirections.NORTH;
In order to make the method simpler, and to avoid calculating the rotation twice—once to test for empty squares and again to rotate the block—you store the current position, rotate the block, and then test to see if the squares of the new block position are empty. If so, you just draw the block in the new position. If not, you restore the previous position.
The basic structure for the method (without the rotation code for each block type) is shown next:
public void Rotate() {
// Store the current block position.
Point OldPosition1 = square1.Location;
Point OldPosition2 = square2.Location;
Point OldPosition3 = square3.Location;
Point OldPosition4 = square4.Location;
RotationDirections OldStatusRotation = StatusRotation;
Hide(GameField.WinHandle);
// Rotate the blocks.
switch(BlockType) {
case BlockTypes.Square:
// Here will go the code to rotate this block type.
break;
case BlockTypes.Line:
// Here will go the code to rotate this block type.
break;
case BlockTypes.J:
// Rotate all squares around square 3.
break;
case BlockTypes.L:
// Rotate all squares around square 3.
break;
case BlockTypes.T:
break;
case BlockTypes.Z:
// Rotate all squares around square 2.
break;
case BlockTypes.S:
// Rotate all squares around square 2.
break;
}
// After rotating the squares, test if they overlap other squares.
// If so, return to original position.
if (!(GameField.IsEmpty(square1.Location.X/squareSize, square1.Location.Y/squareSize) &&
GameField.IsEmpty(square2.Location.X/squareSize, square2.Location.Y/squareSize) &&
GameField.IsEmpty(square3.Location.X/squareSize, square3.Location.Y/squareSize) &&
GameField.IsEmpty(square4.Location.X/squareSize, square4.Location.Y/squareSize))) {
StatusRotation = OldStatusRotation;
square1.Location = OldPosition1;
square2.Location = OldPosition2;
square3.Location = OldPosition3;
square4.Location = OldPosition4;
}
// Draws the square at the correct position.
Show(GameField.WinHandle);
}
Based on each block type and its current status, you can calculate the rotations. There will be three types of rotation:
- Square blocks: These do nothing. Squares don’t need to rotate since they look the same when rotated.
- Line, S, and Z blocks: These will have only two possible directions for rotation, north and east.
- T, J, and L blocks: These will have four different positions—north, east, south, and west.
In any case, you must choose a specific square to stay fixed while the others rotate around it. In the examples that follow, you see what must be in each case statement of the Rotate method, starting with the rotation for a Line block type, represented in Figure 1-27.
The code to implement the rotation of the Line block is shown in the next listing:
switch(StatusRotation) {
case RotationDirections.NORTH:
StatusRotation = RotationDirections.EAST;
square1.Location = new Point
(square2.Location.X-squareSize, square2.Location.Y);
square3.Location = new Point
(square2.Location.X+squareSize, square2.Location.Y);
square4.Location = new Point
(square2.Location.X+2*squareSize,square2.Location.Y);
break;
case RotationDirections.EAST:
StatusRotation = RotationDirections.NORTH;
square1.Location = new Point
(square2.Location.X, square2.Location.Y-squareSize);
square3.Location = new Point
(square2.Location.X, square2.Location.Y+squareSize);
square4.Location = new Point
(square2.Location.X, square2.Location.Y+2*squareSize);
break;
}
Notice that the new square positions are all based on the position of the second square of the block; you just add or subtract the square sizes to move the square up and down (y coordinate) or right and left (x coordinate). In each case, you set the new status of the rotation.

Figure 1-27. Line block: rotation around the second square
Figure 1-28 illustrates the rotation for the Z block type. The S and Z block types rotate in a very similar way.

Figure 1-28. The Z block rotation
Following is the code for the Z block type; the S block follows the same logic.
switch(StatusRotation) {
case RotationDirections.NORTH:
StatusRotation = RotationDirections.EAST;
square1.Location = new Point(square2.Location.X, square2.Location.Y-squareSize);
square3.Location = new Point(square2.Location.X-squareSize, square2.Location.Y);
square4.Location = new Point(square2.Location.X-squareSize, square2.Location.Y+squareSize);
break;
case RotationDirections.EAST:
StatusRotation = RotationDirections.NORTH;
square1.Location = new Point(square2.Location.X-squareSize, square2.Location.Y);
square3.Location = new Point(square2.Location.X, square2.Location.Y+squareSize);
square4.Location = new Point(square2.Location.X+
squareSize, square2.Location.Y+squareSize);
break;
}
As for the T, J, and L block types, the procedure will be a little longer, since you have four directions, but the basic idea remains the same: All squares run around a fixed one. We’ll show you some examples, starting with the T block type rotation, portrayed in Figure 1-29.

Figure 1-29. Rotation of the T block
The next code listing implements the rotation illustrated in Figure 1-29:
switch(StatusRotation) {
case RotationDirections.NORTH:
StatusRotation = RotationDirections.EAST;
square1.Location = new Point(square2.Location.X, square2.Location.Y-squareSize);
square3.Location = new Point(square2.Location.X, square2.Location.Y+squareSize);
square4.Location = new Point(square2.Location.X-squareSize, square2.Location.Y);
break;
case RotationDirections.EAST:
StatusRotation = RotationDirections.SOUTH;
square1.Location = new Point(square2.Location.X+squareSize, square2.Location.Y);
square3.Location = new Point(square2.Location.X-squareSize, square2.Location.Y);
square4.Location = new Point(square2.Location.X, square2.Location.Y-squareSize);
break;
case RotationDirections.SOUTH:
StatusRotation = RotationDirections.WEST;
square1.Location = new Point(square2.Location.X, square2.Location.Y+squareSize);
square3.Location = new Point(square2.Location.X, square2.Location.Y-squareSize);
square4.Location = new Point(square2.Location.X+squareSize, square2.Location.Y);
break;
case RotationDirections.WEST:
StatusRotation = RotationDirections.NORTH;
square1.Location = new Point(square2.Location.X-squareSize, square2.Location.Y);
square3.Location = new Point(square2.Location.X+
squareSize, square2.Location.Y);
square4.Location = new Point(square2.Location.X, square2.Location.Y+squareSize);
break;
}
The code for rotating the J and L blocks is pretty much like the preceding code sample. The main difference is that these blocks will rotate around the third square, as shown in the rotation for the J block illustrated in Figure 1-30.

Figure 1-30. Rotation for the J block
The last two methods for the Block class are discussed in the next section.
This chapter is from Beginning .NET Game Programming in C#, by David Weller, et al., (Apress, 2004, ISBN: 1590593197). Check it out at your favorite bookstore today.
Buy this book now. |
Next: The Show and Hide Methods >>
More .NET Articles
More By Apress Publishing