When I was a kid, my parents bought my brother and I an Atari 5200, our first game console. The game that came with the console was 'Super Break Out'. A game that I remember I really enjoyed playing. Super Brick Breaker is a clone of 'Super Break Out'. Super Brick Breaker is also my first attempt at writing a game using DirectX and Visual C++ 6.0 (I've primarily been a Visual Basic, Delphi, 4GL programmer and database administrator). I've kept the design of the game reasonably simple. There are no off screen surfaces or bitmaps. All of the drawing is done with color fill rectangle 'blits'.
The main reason I chose to build a Break Out clone as my first game was that I thought this type of game would be simple and pretty quick to code. Unfortunately, this proved to not be the case. The part of the game that took the most effort (and time) was the collision detection and collision resolution (i.e. how to reflect the ball) between the ball and the bricks, the ball and the paddle and the ball and playing area boundaries. I am reasonably happy with the current collision detection and collision resolution code, but it is an area that I will continue to improve. (any and all suggestions/criticisms welcome). The main problem with the collision resolution is when the ball intersects symmetrically on a single corner of a brick or the paddle.
Before the project can be compiled, the DirectX SDK (Version 8.0 or higher) must be installed on your machine and the DirectX header files and library files must be at the top of your Visual C++ search path.
Tour of the Super Brick Breaker Game
As previously stated, the game is basically a Break Out clone. The object of the game is to keep the ball in play using the paddle at the bottom of the screen and break (i.e. hit) as many of the bricks as possible. I've designed the game to have three different game options:
- Standard - This game is the basic game. There is only one paddle and one ball in play at one time. The player gets five turns in the game.
- Double - This game is a variation of the Standard game. In this game you have two paddles (one 'floating' above the standard one) and there are two balls in play. The game starts by releasing the first ball. Once that ball has struck the paddle, the second ball is released. Because there are two balls in play, the player only receives three turns.
- Escape - This game is similar to the basic game in that there is only one paddle and the player receives five turns. However, within the game bricks, there are two balls 'waiting to escape'. The balls are trapped in the bricks until the enough bricks are removed for the balls to escape. Once they 'escape', the balls are then in play.
Basic Game Play
Points are awarded for each brick hit by a ball in play. The point value is determined by the color of the brick:
- Blue - 2 points
- Green - 4 points
- Yellow - 6 points
- Red - 8 points
The color of the brick struck also affects the reflect velocity of the ball. Bricks with a higher point value will reflect the ball at a faster velocity, but all bricks of the same color reflect at the same velocity (i.e. the ball does get faster after each red brick is struck, only after the first red brick). If a lower point brick is struck, the velocity is dropped by one 'unit' (i.e. if a red brick is struck and then a blue brick is struck, the ball will reflect off the blue brick at the velocity of a yellow brick. If another blue brick is struck, the velocity is that of a green brick.)
If a ball in play strikes the top boundary of the playing area, the paddle is reduced to around 1/3 of the standard width of the paddle. The ball will still reflect at normal velocity from the bricks.
Starting with Level 3 of all games, the number of Red brick rows will increase (overwriting the other colors). So in Level 3 there will be three Red brick rows (only 1 yellow) and in Level 4 there will be four Red brick rows (no yellow) and so on until all the rows are then Red.
The game ends after the last ball in play has gone out of play by going below the bottom the paddle and there are no turns remaining. The turn ends after the last ball in play has gone out of play by going below the bottom the paddle (note for Double and Escape games, there may be more than one ball in play).
Tour of the Super Brick Breaker Code
The game is constructed in a library of classes. Most functionality is contained in one or more of these classes. The main classes are
(which inherits from
). These two classes contain the majority of 'game engine' functionality. The remaining classes (
) are used to describe the main game objects that are processed during the execution of the game. Each of the classes is described in more detail below.
is a very simple class. Its sole purpose is to hold information about the current position, velocity and state of a ball. The majority of the methods are simply 'Get/Set' type methods for retrieving and setting the various properties of the ball.
The state of the ball determines how the ball with be processed during execution of the game. During execution of the main menu and start of a turn, the balls have a state of IDLE. During this state, the balls are not moved or drawn during the processing of the frame. When playing Escape, the captive balls have a state of CAPTIVE. At this state, the balls are confined to the rectangle defined by the member variable
. Balls with a state of CAPTIVE are drawn and processed each frame, including when the main menu is being displayed. During general game play, any 'non-captive' and 'non-lost' balls have a state of FREE. This is the main state and a ball in this state is processed and drawn each frame.
is also a reasonably simple class. This class is responsible for storing information about the current position, current velocity, current length and bounds of the paddle in the playing area. Most of the methods in the class are simple 'Get/Set' type methods.
Methods of Interest
|IsPaddleAtPosition||This is one the main methods used by the game to test for a collision between the ball and the paddle. It determines whether or not one of the paddle rectangles (there is more than one when playing Double) is at the passed X, Y coordinates. This method is called by the |
CheckHitPaddle method of the
CSuperBrickBreaker class. If an intersection occurred, a RECT of the paddle rectangle intersected is returned as a pointer argument.
CBricks is a more interesting class. The main member variable of the
CBricks class is
m_bricks, a dynamic array of
TBrick structures. The array of bricks is allocated during execution of method
InitializeBricks based on the number of rows and columns set in method
TBricks structure contains all the properties for storing the current position, state, point value and reflect velocity of each brick.
The other main member variables of interest are
m_brick_ver_space. These variables define how the array of bricks will be initialized, positioned and spaced in the playing area during execution of method
The array of row colors (member variable
m_row_colors); point values (member variable
m_row_points) and reflect velocities (member variable
m_row_vel) are dynamically allocated based on the number rows and values passed in method
SetRowColProperties. It is assumed the array lengths passed to the procedure match the number of rows of bricks to be created.
Methods of Interest
|IsBrickAtPosition||This is one the main methods used by the game to test for a collision between the ball and the bricks. It determines whether or not one of the bricks is at the passed X, Y coordinates. This method is called by the |
IsBrickAtBallLocation method of the CSuperBrickBreaker class. If an intersection occurred, a
TBrick structure of the brick intersected is returned as a pointer argument.
This class provides the main functionality for using DirectX in a game. It provides the variables and functions for initialization and shutdown of the DirectX components DirectDraw and DirectInput. The initialization of the DirectX components in this are fairly static. The DirectDraw surface color depth must be 16-bit, there is always only one back buffer and the keyboard is the only input device initialized with DirectInput.
CDirectDrawGame provides all the methods for drawing to the screen (DirectDraw for rectangles and GDI for text), retrieving the keyboard data from DirectInput and playing sounds through DirectSound. It also provides some 'utility' functions to manage the frame rate, 16-bit color bit masking/shifting and the setting of the class member variables.
Methods/Variables of Interest
|InitializeDirectDraw||This method initializes and creates instances of all the DirectDraw member variables for use by the application/game. It is assumed all the relevant member variables (window handle, application instance, etc.) have been set through the relevant class method.|
|InitializeDirectInput||This method initializes and create instance of all the DirectInput member variables for use by the application/game. The only input device registered is the keyboard.|
|StartScene||This method is designed to be the first method called when starting to draw a new frame/scene. This method simply erases the display on the back buffer by doing a black color fill using the DirectDraw function |
|DrawRectangle||This method is the workhorse of the game. As stated before there are no bitmaps, so everything on the screen (other than the text) is drawn through this method. It draws a rectangle at the specified location in the specified color on the back buffer using the DirectDraw function |
|EndScene||This method is designed to be the last method called after all required drawing has been performed for the frame/scene. This method transfers the display on the back buffer to the primary surface using DirectDraw function |
|StartGDIDrawing||This method is used to capture the required variables from the back buffer (namely the device context) for using GDI drawing functions on a DirectDraw surface. This method must be called before calling |
|DrawGDIText||This method is used to draw the specified text in the specified RGB color on the back buffer using GDI function |
|EndGDIDrawing||This method is used to release all the GDI variables acquired by method |
|CalculateRGBBitShift||This method is used by the |
InitializeDirectDraw method to determine how 16-bit colors need to be transformed from RGB values to a single DWORD color using bit masking and bit shifting. These values are stored in member variables and used by method
|RGBto16BitColor||This method converts a RGB color to a single DWORD color that can be used by the DirectDraw drawing functions. This methods converts the values by using the bit shifting variables determined by method |
This class is the main code of the game. It contains the all the game processing code including all drawing and handling of the game objects. As this the main class, it is also the largest and most complex class in the game. This class contains many member variables for managing the current overall state of the game, the playing area boundaries, the colors to be used for drawing the game objects and the game objects themselves.
Methods of Interest
|InitializeBalls||This method is used to initialize the ball array (member variable m_ball) with the starting position and state of the balls to be used based on the current type and current state of the game.|
|InitializeBricks||This method sets the row and column properties of the brick (ie. the number of rows, number of columns and the color, point value and reflect velocity of each row of bricks). This method also removes the necessary bricks for the captive balls when playing 'Escape'.|
|InitializeColors||This method converts the RGB color values to DWORD colors that can then be used by the DirectDraw drawing function. It converts the colors using method |
RGBto16BitColor (from parent class
|InitializePaddle||This method initializes the position and size of the paddle before the start of each turn. (The paddle is always restored to its full width and moved to the middle of the playing area at the start of a turn).|
|InitializePlayingArea||This method initializes the rectangle that define the playing boundaries and the area surrounded by the boundaries. These rectangles are used by other methods in the class to control the movement of the balls and paddles in the playing area.|
|ProcessBalls||This method is probably the most complex method in the entire game. ProcessBalls is responsible for controlling the behavior of the balls in the playing area. It moves the balls based on their current velocity and is the starting point for resolving any collisions that occur between the balls and either the playing area boundary, the bricks or the paddle.|
|ProcessPaddle||This method moves the paddle across the bottom of the playing area based on the current velocity of the paddle (10 pixels if right arrow is pressed, -10 if left arrow key is pressed).|
|ProcessStateChange||This method handles are necessary processing when the game needs to transition game states. The valid game states are |
|CheckHitPaddle||This method is one the collision detection routines. It checks whether or not any of of the ball corners has intersected any of the paddle rectangles. If an intersection has occurred, method |
BallRectIntersect is called to determine how the ball intersected the paddle and ultimately how the ball will be reflected off the paddle.
|CheckHitBrick||This method is one the collision detection routines. It checks whether or not any of of the ball corners has intersected any of the visible bricks by calling method |
IsBrickAtBallLocation. If an intersection has occurred, method
BallRectIntersect is called to determine how the ball intersected the brick and ultimately how the ball will be reflected off the brick.
|BallRectIntersect||This method is also one of the collision detection routines. It determines how the passed ball (passed as a RECT) has intersected the passed game object (a brick or a paddle also passed as a RECT). Using the ball corners it determines if the ball intersected the top, right, bottom or left side of the game object and then reflects the balls in the appropriate direction.|
|IsBrickAtBallLocation||This method is the final collision detection routine. It checks whether or not any of of the ball corners has intersected any of the visible bricks.|
|GameInitialize||This method is responsible for the initialization of the game variables that apply to all of the game types. It initializes the colors, the fonts that will be used for drawing the menus and the heading line, the playing area, and the default properties of the bricks.|
|GameMain||This method is the main game processing function. The method is called every frame and drives the drawing and processing of each scene.|
WinMain does not contain a class but it still deserves a quick mention. WinMain contains the application entry point and declares the main window for the game. It also contains the Windows message loop and the message handling function. WinMain also declares, initializes and uses the CSuperBrickBreaker class to drive the game.
Hopefully that gives you a good idea about what the game is about and how it has been constructed. The code is pretty well commented so anything I didn't mention here should be pretty well described in the source. If you have any questions/comments please feel free to either post a message or send me an email.
13-Jan-2004 - Initial Release of Article