Game Development of .Nettrix: GDI+ and Collision Detection - Final Version: Coding the GameField Class and the Game Engine
(Page 18 of 22 )
To finish your program, you’ll have to complete the code for the game engine and the GameField class, as shown in the next sections.
GameField Class Examine the code to implement the public properties and methods for the GameField class, as defined in your game project.
public class GameField {
public const int Width = 16;
public const int Height = 30;
public const int SquareSize = 10;
public static System.IntPtr WinHandle;
public static Color BackColor;
private static Square[,] arrGameField = new Square[Width, Height];
public static bool IsEmpty(int x, int y) {…}
public static int CheckLines() {…}
public static void StopSquare(Square Square, int x, int y) {…}
public static void Redraw() {…}
public static void Reset() {…}
}
The GameField interface shown in the preceding code has its members (properties and methods) defined in the class diagram proposed in the game project, plus the new properties and methods defined in the stubs you created previously. Although it isn’t unusual for such changes to happen during a real-life project, it should be one of your goals to define a clear and comprehensive project before starting to code. Remember, changing a project is far easier (and cheaper) than changing and adapting code; and if there are many unpredictable changes to code, the project tends to be more prone to errors and more difficult to maintain. (We refer to this as the Frankenstein syndrome: The project will no longer be a single and organized piece of code, but many not so well-sewed-on parts.)
One interesting point about this class is that every member is declared as static! In other words, you can access any method or property of the class without creating any objects. This isn’t the suggested use of static properties or methods; you usually create static class members when you need to create many objects in the class, and have some information—such as a counter for the number of objects created, or properties that, once set, affect all the objects created.
The next sections discuss the GameField class methods, starting with the IsEmpty method.
The IsEmpty Method
The first class method, IsEmpty, must check if a given x,y position of the game array (arrGameField) is empty. The next method, CheckLines, has to check each of the lines of the array to see if any one of them is full of squares, and remove any such lines.
Since the arrGameField is an array of Square objects, you can check if any position is assigned to a square with a simple test.
public static bool IsEmpty(int x, int y) {
return arrGameField[x,y] != null;
}
Some extra tests should be done to see if the x or the y position is above (or below) the array boundaries.
Although in this game you don’t need high-speed calculations, you can use an improved algorithm for collision detection, so that you can see a practical example of using these algorithms.
You can improve the performance of the IsEmpty and CheckLines functions using an array of bits to calculate the collisions. Since your game field is 16 squares wide, you can create a new array of integers, where each bit must be set if there’s a square associated with it. You still must maintain the arrGameField array, because it will be used to redraw the squares when a line is erased or the entire game field must be redrawn (for example, when the window gets the focus after being below another window).
The array that holds the bits for each line must have the same Height as the arrGameField, and will have just one dimension, since the Width will be given for the bits in each integer (16 bits per element). When a square stops inside the game field, a bit will be set (inside the StopSquare method) that will indicate a square is occupying that spot. The array definition is shown in the next code line:
private static int[] arrBitGameField = new int[Height];
And the IsEmpty function is as follows:
public static bool IsEmpty(int x, int y) {
// If the y or x is beyond the game field, return false.
if ((y<0||y>=Height)||(x<0||x>=Width)) {
return false;
}
// Test the xth bit of the yth line of the game field.
else if((arrBitGameField[y] & (1<<x)) !=0) {
return false;
}
return true;
}
In this sample code, the first if statement checks whether the x and y parameters are inside the game field range. The second if statement deserves a closer look: What is arrBitGameField[y] & (1<< x) supposed to test? In simple words, it just checks the xth bit of the arrBitGameField[y] byte.
This piece of code works well because the comparison operators work in a binary way. The & operator performs a bit-to-bit comparison, then returns a combination of both operands. If the same bit is set in both operands, this bit will be set in the result; if only one or none of the operators has the bit set, the result won’t have the bit set. Table 1-4 shows the operands’ bits for some & comparisons.
Table 1-4. Bits and Results for Some & Operations
| NUMBERS | BITS |
| 1 & 2 = 0 | 01 & 10 = 0 (false) |
| 3 & 12 = 0 | 0011 & 1100 = 0000 (false) |
| 3 & 11 = 3 | 0011 & 1011 = 0011 (true) |
In your code, if you want to check, for example, the seventh bit, the first operand must be the array element you want to check, arrBitGameField[y], and the second operand must have the bits 00000000 01000000 (16 bits total, with the seventh one checked).
If you did your binary homework well, you’d remember that setting the bits one by one results in powers of 2: 1, 2, 4, 8, 16, and so on, for 00001, 00010, 00100, 01000, 10000, etc. The easiest way to calculate powers of 2 is just to shift the bits to the left; fortunately for you, C# has operators that will do bit shifting (<< for shifting bits to the left, and >> for shifting bits to the right).
Looking again at the second if statement, everything should make sense now:
- arrBitGameField[y]: The 16 bits of the yth line of the game field.
- 1<<X: Shifts one bit over to the xth position.
- arrBitGameField[y] & (1<< x): If the xth bit of the array element is set, then the test will return a nonzero number; any other bit set won’t affect the result, since the second operand has only the xth bit set.
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 CheckLines Method >>
More .NET Articles
More By Apress Publishing