Example: Freakout should be paused before I discussed Windows, DirectX and 3D graphics, first show you a full game - although it is simple, there is no doubt that is a complete game. You will see an actual game loop and some graphical function calls, and finally you can compile. Not bad right? Come with me! The problem is that we will talk about the first chapter now. I shouldn't use the content in the following chapters ... this is a bit like cheating, right? Therefore, I decided to make you accustomed to using Black Box API to make game programming. Based on this requirement, I want to mention a question "To make a 2D game similar to Breakout, what is the minimum requirement?" We really need the following features: • Switch in any image mode? Draw a rectangle of various colors on the screen? Get the keyboard input? Use some timed function to synchronize the game loop? Draw a colorful text string on the screen, so I built a library named BlackBox.cpp | h. It encapsulates a set of DirectX function sets (limited to DirectDraw) and contains support code that implements the desired function. Wonderful place is that readers don't need to see these code at all, just use these functions in accordance with the function prototype, and connect to the BlackBox.cpp | H to generate .exe executable. Based on the BlackBox library, I wrote a game named Freakout, which demonstrates many of the concepts discussed in this chapter. The Freakout game includes all the main components of the true game, including: game cycle, scoring, level, and even mini physical models written for the ball. It's really cute. Figure 1.9 is a screen screen in a game run. Obviously it is not arkanoid (classic brick game), but 4 hours of work is not good! Figure 1.9 The screen capture of the Freakout game before reading the game source code, I hope the reader can see how the projects and games are coordinated. See Figure 1.10. Figure 1.10 Freakout's structure As can be seen from the figure, the game consists of the following file: freakout.cpp - the main logic of the game, use BlackBox.cpp, create a minimized Win32 application. BlackBox.cpp - Game Library (please don't peek :). BlackBox.h - the header file of the game library. DDRAW.LIB - DirectDraw input library for generating applications. It does not contain real DirectX code. It is mainly used as an intermediate library that allows users to call, and then calls the DDRaw.dll dynamic link library that is actually working. It can be found in the lib sub-directory in the DirectX SDK installation directory. DDRAW.DLL - Runtime's DirectDraw library, actually contains a COM executor that calls DirectDRAW interface functions via DDRAW.LIB input library. You don't have to worry about it; just confirm that DirectX is already installed. In order to build BLACKBOX.CPP and FREAKOUT.CPP, connect to the DDRAW.LIB library file, and ensure that BlackBox.h is in the header file search path or working directory so that the compiler can find it correctly. Now we have roughly understand the architecture of Freakout. Let's take a look at the Blackout.h header, see which functions it contains. Program List 1.2 Blackout.h Header // BlackBox.h - Header File for Demo Game Engine LIBRARY
// Watch for multiple inclusions # ifndef BlackBox # define blackbox // defines
// default screen size # define screen_width 640 // size of screen # define screen_height 480 # define screen_bpp 8 // bits per pixel # define max_colors 256 // Maximum Colors
// macros /
// these read the keyboard asynchronously # define KEY_DOWN (vk_code) ((GetAsyncKeyState (vk_code) & 0x8000) 1:? 0) #define KEY_UP (vk_code) ((GetAsyncKeyState (vk_code) & 0x8000) 0:? 1)
// Initializes A Direct Draw Struct # define DD_INIT_STRUCT (DDStruct) {MEMSET (& DDStruct, 0, SizeOf (DDStruct)); DDStruct.dwsize = SizeOf (DDStruct);}
// Types //
// Basic Unsigned TypeStypedef Unsigned Short Ushort; type; type;
// Externals //
extern LPDIRECTDRAW7 lpdd; // dd objectextern LPDIRECTDRAWSURFACE7 lpddsprimary; // dd primary surfaceextern LPDIRECTDRAWSURFACE7 lpddsback; // dd back surfaceextern LPDIRECTDRAWPALETTE lpddpal; // a pointer dd paletteextern LPDIRECTDRAWCLIPPER lpddclipper; // dd clipperextern PALETTEENTRY palette [256]; // color paletteextern PALETTEENTRY save_palette [256]; // used to save palettesextern DDSURFACEDESC2 ddsd; // a ddraw surface description structextern dDBLTFX ddbltfx; // used to fillextern DDSCAPS2 ddscaps; // a ddraw surface capabilities structextern HRESULT ddrval; // result back from dd callsextern DWORD Start_Clock_count; // ue f c, _,;;;;;;;;;;;;;;;;;;;;;; THESE Are Overwritten Globally By DD_INIT () Extern Int Screen_Width, // Width of Screen Screen_HEight, // Height Of Screen Screen_bpp; // Bits Per Pixel
// Prototypes /
// DirectDraw functionsint DD_Init (int width, int height, int bpp); int DD_Shutdown (void); LPDIRECTDRAWCLIPPER DD_Attach_Clipper (LPDIRECTDRAWSURFACE7 lpdds, int num_rects, LPRECT clip_list); int DD_Flip (void); int DD_Fill_Surface (LPDIRECTDRAWSURFACE7 lpdds, int color) ;
// General Utility FunctionsDword Start_clock (void); DWORD GET_CLOCK (Void); DWORD WAIT_CLOCK (DWord Count);
// graphics functionsint Draw_Rectangle (int x1, int y1, int x2, int y2, int color, LPDIRECTDRAWSURFACE7 lpdds = lpddsback); // gdi functionsint Draw_Text_GDI (char * text, int x, int y, COLORREF color, LPDIRECTDRAWSURFACE7 lpdds = lpddsback INT Draw_text_gdi (Char * Text, INT X, INT Y, INT Color, LPDirectDrawsurface7 LPDDS = LPDDSBACK);
#ENDIF now, don't spend too much time to study the brain juice to study the program code here, you can't understand what the mysterious global variables are not important. Let's take a look at these functions. As you want, there is a full function needed to implement our simple graphical interface. Based on this graphical interface and the minimized Win32 application (the less well we want to do, the better the Windows programming work), I created the game Freakout.cpp, as shown in the list 1.3. Please take a look carefully, especially the game main loop and call to the game processing function. Program List 1.3 Freakout.cpp Source File
// incrudes ///
#define Win32_Lean_and_mean // Include All Macros # deflish initguid // incrude all guids
#include
// incrude important windows stuff
#include
#include
#include
// include Important C / C stuff
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// DirectX Includes
#include "blackbox.h" // Game Library Includes
// defines
// defines for windows # define window_class_name "win3dclass" // class name
#define window_width 640 // size of window # define window_height 480
// states for game loop # define game_State_init 0 # define game_state_start_ser 1 # define game_state_run 2 # define game_state_shutdown 3 # define game_state_exit 4
// block defines # define num_block_rows 6 # define num_block_columns 8
#define BLOCK_WIDTH 64 # define BLOCK_HEIGHT 16 # define BLOCK_ORIGIN_X 8 # define BLOCK_ORIGIN_Y 8 # define BLOCK_X_GAP 80 # define BLOCK_Y_GAP 32 // paddle defines # define PADDLE_START_X (SCREEN_WIDTH / 2 - 16) #define PADDLE_START_Y (SCREEN_HEIGHT - 32); # define PADDLE_WIDTH 32 # Define Paddle_height 8 # Define Paddle_color 191
// Ball defines # define ball_start_y (screen_height / 2) #define ball_size 4
// Prototypes /
// Game consoleint game_init (void * parms = null); int game_shutdown (void * parms = null); int game_main (void * parms = null);
// globals
HWnd main_window_handle = null; // save the window Handlehinstance main_instance = null; // save the instanceint game_state = game_state_init; // starting state
INT PADDLE_X = 0, PADDLE_Y = 0; // TRACKS POSITION OF PADDLEINT BALL_X = 0, Ball_Y = 0; // TRACKS POSITION OF Ballint Ball_DX = 0, Ball_Dy = 0; // Velocity Of Ballint Score = 0; // THE SCOREINT Level = 1; // the current level blocks_hit = 0; // TRACKS NUMBER OF BLOCKS HIT
// this Contains the Game Grid Data
Uchar blocks [num_block_rows] [NUM_BLOCK_COLUMNS];
// functions //
LRESULT CALLBACK WindowProc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {// this is the main message handler of the systemPAINTSTRUCT ps; // used in WM_PAINTHDC hdc; // handle to a device context
// What is the messagewitch (msg) {copy wm_create: {// do INITIALIZATION stuff Here Return (0);} Break; Case WM_Paint: {// Start Painting HDC = BeginPaint (HWND, & PS);
// the window is now validated
// end Painting endpaint (hwnd, & ps); return (0);} Break;
Case WM_DESTROY: {// kill the application postquitmessage (0); return (0);} Break
DEFAULT: BREAK;
} // End Switch
// Process Any Messages That We Didn't Take Care Ofreturn (DEFWINDOWPROC (HWND, MSG, WPARAM, LPARAM);
} // End WinProc
// WinMain
Int WinApi Winmain (Hinstance Hinstance, LPSTANCE HPREVINSTANCE, LPSTR LPCMDLINE, INT NCMDSHOW) {// this is the winmain function
WNDCLASS winclass; // this will hold the class we createHWND hwnd; // generic window handleMSG msg; // generic messageHDC hdc; // generic dcPAINTSTRUCT ps; // generic paintstruct // first fill in the window class structurewinclass.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW; winclass.lpfnWndProc = WindowProc; winclass.cbClsExtra = 0; winclass.cbWndExtra = 0; winclass.hInstance = hinstance; winclass.hIcon = LoadIcon (NULL, IDI_APPLICATION); winclass.hCursor = LoadCursor (NULL, IDC_ARROW); WinClass.hbrbackground = (Hbrush) getStockObject (black_brush); WinClass.lpsz GeneName = NULL; WINCLASS.LPSZCLASSNAME = WINDOW_CLASS_NAME
// register the window classif (! registerclass (& winclass)) Return (0);
// create the window, note the use of WS_POPUPif ((hwnd = CreateWindow (WINDOW_CLASS_NAME, // class "WIN3D Game Console", // title WS_POPUP |! WS_VISIBLE, 0,0, // initial x, y GetSystemMetrics (SM_CXSCREEN) , // Initial Width GetSystemmetrics (SM_CYSCREEN), // Initial Height Null, // Handle To Menu Hinstance, // Instance Null)) // CREATION PARMSRETURN (0); // Hide Mouseshowcursor FALSE);
// Save the window handle and instance in a globalmain_window_handle = hwnd; main_instance = hinstance;
// Perform All Game Console Specific InitializationGame_init ();
// Enter Main Event loopWhile (1) {IF (PEEKMESSAGE (& MSG, NULL, 0, 0, PM_REMOVE)) {// Test if this is a quit if (msg.Message == WM_QUIT) Break;
// Translate Any Accelerator Keys TranslateMessage (& MSG);
// send the message to the window proc DispatchMessage (& msg);} // end if // main game_main ();
} // End while
// Shutdown game and release all resourcesgame_shutdown ();
// Show Mouseshowcursor (True);
// Return to Windows Like thisreturn (msg.wparam);
} // End WinMain
// T3DX Game Programming Console functions
INT GAME_INIT (VOID * PARMS) {// this function is where you do all the initialization // for your game
// Return SuccessReturn (1);
} // end game_init
///
INT GAME_SHUTDOWN (VOID * PARMS) {// this function is where you shutdown your game and // Release All Resources That you allocated
// Return SuccessReturn (1);
} // end game_shutdown
///
void Init_Blocks (void) {// initialize the block fieldfor (int row = 0; row Void draw_blocks (void) {// this function draws all the block in row major factory x1 = block_origin_x, // used to track current position y1 = block_origin_y; // Draw All the block blocksfor (int Row = 0; ROW // Draw this Row Of Blocks for (INT COL = 0; Col Draw_Rectangle (x1, y1, x1 block_width, y1 block_height, blocks [row] [col]);} // end if // Advance Column Position X1 = Block_x_gap;} // end for col // advance to next row position y1 = block_y_gap; } // end for row } // end Draw_blocks /// Void process_ball (void) {// this function tests if the ball HAS HIT A Block or the paddle //ness and the block is removed from // The Playfield Note: Very Cheeesy Collision Algorithm :) // First Test for Ball Block Collisions // The Algorithm Basically Tests The Ball Against Each // Block's Bounding Box this IS inefficient, but easy to //ware, later we'll see a better Way INT x1 = block_origin_x, // current rendering position y1 = block_origin_y; INT BALL_CX = BALL_X (Ball_SIZE / 2), // Computer Center of Ball Ball_CY = Ball_Y (Ball_Size / 2); // Test of the Ball Has Hit The Paddleif (Ball_Y> (Screen_HEight / 2) && ball_dy> 0) {/ / extract leading Edge of Ball INT X = Ball_x (Ball_size / 2); int y = ball_y (ball_size / 2); // Test for collision with paddle if (x> = paddle_x && x <= paddle_x paddle_width) && (y> = paddle_y && y <= paddle_y paddle_height) {// reflect ball ball_dy = -ball_dy; // push ball out of paddle Since it make "; // add a little english to ball based on motion of paddle if (key_down (vk_right)) BALL_DX - = (RAND ()% 3); ELSE IF (key_down (vk_left)) BALL_DX = (Rand ()% 3); Else BALL_DX = (- 1 rand ()% 3); // test if there are no blocks, if so send a message // to game loop to start another level if (blocks_hit> = (NUM_BLOCK_ROWS * NUM_BLOCK_COLUMNS)) end if // {game_state = GAME_STATE_START_LEVEL;; level } // make a little noise messagebeep (mb_ok); // Return Return; } // end if } // end if // now scan thru all the block and see if Ball Hit Blocksfor (int Row = 0; ROW // Scan this Row Of Blocks for (int coL = 0; col // Bounce the ball ball_dy = -ball_dy; // Add a little english ball_dx = (- 1 rand ()% 3); // make a little noise messagebeep (mb_ok); // Add Some Points Score = 5 * (Level (ABS (Ball_DX))); // That's it - no more block return; } // end if } // end if // Advance Column Position X1 = Block_x_gap;} // end for col // advance to next row position y1 = block_y_gap; } // end for row} // end process_ball /// INT GAME_MAIN (VOID * PARMS) {// this is the workhorse of Your Game It Will Be Called // Continuously in real-time this is like main () in c // all the calls for your game game go here! CHAR BUFFER [80]; // used to print text // What state is the game in? if (Game_State == Game_State_init) {// Initialize Everything Here Graphics DD_INIT (Screen_Width, Screen_HEight, Screen_bpp); // seed the random number generator // so game is Different Each Play SRAND (START_CLOCK ()); // set the paddle position here to the middle bottom paddle_x = PADDLE_START_X; paddle_y = PADDLE_START_Y; // set ball position and velocity ball_x = 8 rand ()% (SCREEN_WIDTH-16); ball_y = BALL_START_Y; ball_dx = -4 rand ()% (8 1); ball_dy = 6 rand ()% 2; // transition to start level state game_state = game_state_start_level; } // end ifelseif (Game_State == Game_State_Start_Level) {// Get a new level ready to run // Initialize the blocks init_blocks (); // reset block counter block_hit = 0; // transition to run statate_state = game_state_run;} // end if /// elseif (Game_State == Game_State_Run) {// Start The Timing Clock Start_clock (); // Clear Drawing Surface for the next frame of animation draw_rectangle (0, 0, screen_width-1, screen_height-1,200); //move the paddle if (key_down (vk_right)) {//move paddle to right paddle_x = 8; // Make Sure Paddle Doesn't Go Off Screen IF (Paddle_x> (Screen_Width-Paddle_WIDTH)) PADDLE_X = Screen_Width-Paddle_WIDTH; } // end if Else if (key_down (vk_left)) {//move paddle to right paddle_x- = 8; // Make Sure Paddle Doesn't Go Off Screen IF (Paddle_x <0) paddle_x = 0; } // end if // Draw Blocks Draw_blocks (); //move the ball ball_x = Ball_dx; ball_y = Ball_dy; // Keep Ball On Screen, if The Ball Hits The Edge of // Screen Ten Bounce IT by Reflecting ITS Velocity IF (Ball_x> (Screen_Width - Ball_size) || Ball_X <0) {// Reflect X-Axis Velocity Ball_DX = - Ball_dx; // update position ball_x = Ball_dx;} // end if // now y-axis if (Ball_Y <0) {// Reflect y-axis velocity ball_dy = -ball_dy; // update position ball_y = ball_dy;} // end_dy;} // end IF else // Penalize Player for missing the ball if (spreen_height - ball_size) {// reflect y-axis velocity ball_dy = -ball_dy; // update position ball_y = BALL_DY; // minus the score- = 100; } // end if // next Watch Out for Ball Velocity Getting Out of Handiff (Ball_DX> 8) Ball_DX = 8; ELSE IF (Ball_DX <-8) Ball_DX = -8; // Test if Ball Hit any blocks or the paddle process_ball (); // Draw the paddle and shadow draw_rectangle (paddle_x-8, paddle_y 8, paddle_x paddle_width-8, paddle_y paddle_height 8, 0); Draw_Rectangle (Paddle_x, Paddle_y, Paddle_x Paddle_WIDTH, PADDLE_Y PADdle_HEIGHT, PADDLE_COLOR); // DRAW THE BALL DRAW_RECTANGLE (Ball_X-4, Ball_Y 4, Ball_x Ball_Size 4, 0); Draw_Rectangle (Ball_x, Ball_Y, Ball_x Ball_Size, Ball_Y Ball_size, 255); // Draw the Info Sprintf (Buffer, "F R E a K O U T Score% D // Level% D", Score, Level); DRAW_TEXT_GDI (Buffer, 8, Screen_HEIGHT-16, 127); // Flip The Surfaces DD_FLIP (); // Sync to 33ish fps Wait_Clock (30); // Check if User is trying to exit if (key_down (vk_escape)) {// send message to windows to exit postmessage (main_window_handle, wm_destroy, 0,0); // set exit statate game_state = game_state_shutdown;} // endiff } // end if /// elseif (Game_State == Game_State_Shutdown) {// in this state shut everying down and release resources dd_shutdown (); // Switch to EXIT State Game_State = game_state_exit; } // end if // Return SuccessReturn (1);