程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> Win32 SDK 打磚塊游戲

Win32 SDK 打磚塊游戲

編輯:C++入門知識

前一陣子學習Win32 SDK 時自己寫過一個拼圖游戲作為練習,基本功能是都完成了,但到後來隨著代碼量的增多,代碼的組織上有點混亂。考慮到是第一次獨立用Win32 SDK編寫程序,就先放過了。但總覺的不爽。

最近看到一本名為《Windows 游戲編程大師技巧》的牛書,看到第一章打磚塊的示例,欲效仿其組織代碼的技巧,故將其源代碼分拆成容易理解的若干部分,修改抄錄於下,供他日重讀或供無緣見到此書者查閱。

程序版權歸原作者所有,吾僅是學習模仿,不敢掠美。

但源程序使用到了DirectX SDK,由於暫時不想涉及D3D,故改為用Win32 API 繪圖。當然以後有空,或會學習一下DirectX SDK~


我們需要的是下面幾個功能:

1)切換至任意圖像模式

2)在屏幕上繪制各種顏色的矩形

3)獲取鍵盤輸入

4)使用一些定時函數同步游戲循環

5)在屏幕上畫彩色字符串


1. 程序主框架(原程序為全屏隱藏鼠標,為調試簡單,暫時改為非全屏不隱藏鼠標)

[cpp] 
/*
 * FreakOut.cpp (改編自《Windows 游戲編程大師技巧》第一章示例程序)
 * 2012-11-29
 */ 
 
/* INCLUDES *****************************************************************************/ 
#include <windows.h> 
 
/* DEFINES ******************************************************************************/ 
// defines for windows 
#define WINDOW_CLASS_NAME   TEXT("WIN32CLASS") 
#define WINDOW_WIDTH        640 
#define WINDOW_HEIGHT       480 
 
// states for game loop 
#define GAME_STATE_INIT         0 
#define GAME_STATE_START_LEVEL  1 
#define GAME_STATE_RUN          2 
#define GAME_STATE_SHUTDOWN     3 
#define GAME_STATE_EXIT         4 
 
 
/* GLOBALS *******************************************************************************/ 
HWND        main_window_handle  = NULL;             // save the window handle 
HINSTANCE   main_instance       = NULL;             // save the instance 
int         game_state          = GAME_STATE_INIT;  // starting state 
 
 
/* WINDPROC ******************************************************************************/ 
LRESULT CALLBACK WindowProc(HWND    hwnd, 
                            UINT    msg, 
                            WPARAM  wparam, 
                            LPARAM  lparam) 

    // this is the main message handler of the system 
    PAINTSTRUCT ps; 
    HDC         hdc; 
 
    switch (msg) 
    { 
    case WM_CREATE: 
        { 
            return 0; 
        } 
 
    case WM_PAINT: 
        { 
            hdc = BeginPaint(hwnd, &ps); 
            EndPaint(hwnd, &ps); 
            return 0; 
        } 
 
    case WM_DESTROY: 
        { 
            PostQuitMessage(0); 
            return 0; 
        } 
 
    default: 
        break; 
    } 
 
    return DefWindowProc(hwnd, msg, wparam, lparam); 

 
 
/* WINMAIN *******************************************************************************/ 
int WINAPI WinMain(HINSTANCE hinstance, 
                   HINSTANCE hprevinstance, 
                   LPSTR lpcmdline, 
                   int ncmdshow) 

    WNDCLASS    winclass; 
    HWND        hwnd; 
    MSG         msg; 
    HDC         hdc; 
    PAINTSTRUCT ps; 
 
    /* CS_DBLCLKS Specifies that the window should be notified of double clicks with 
     * WM_xBUTTONDBLCLK messages
     * CS_OWNDC標志,屬於此窗口類的窗口實例都有自己的DC(稱為私有DC),私有DC僅屬於該窗口實例,
     * 所以程序只需要調用一次GetDC或BeginPaint獲取DC,系統就為窗口初始化一個DC,並且保存程序
     * 對其進行的改變。ReleaseDC和EndPaint函數不再需要了,因為其他程序無法訪問和改變私有DC。
     */ 
    winclass.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.lpszMenuName   = NULL; 
    winclass.lpszClassName  = WINDOW_CLASS_NAME; 
 
    // register the window class 
    if (!RegisterClass(&winclass)) 
        return 0; 
 
    // Create the window, note the use of WS_POPUP 
    hwnd = CreateWindow( 
        WINDOW_CLASS_NAME,          // class 
        TEXT("WIN32 Game Console"), // title 
        WS_OVERLAPPEDWINDOW,        // style 
        0, 0,                       // initial x, y  
        WINDOW_WIDTH,               // initial width 
        WINDOW_HEIGHT,              // initial height 
        NULL,                       // handle to parent 
        NULL,                       // handle to menu 
        hinstance,                  // instance 
        NULL);                      // creation parms 
 
    if (! hwnd) 
        return 0; 
 
    ShowWindow(hwnd, ncmdshow); 
    UpdateWindow(hwnd); 
 
    // hide mouse 
    //ShowCursor(FALSE); 
 
    // save the window handle and instance in a global 
    main_window_handle  = hwnd; 
    main_instance       = hinstance; 
 
    // perform all game console specific initialization 
    //Game_Init(); 
 
    // enter main event loop 
    while (1) 
    { 
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 
        { 
            // test if this is a quit msg 
            if (msg.message == WM_QUIT) 
                break; 
 
            // translate any accelerator keys 
            TranslateMessage(&msg); 
 
            // send the message to the window proc 
            DispatchMessage(&msg); 
        } 
 
        // main game processing goes here 
        //Game_Main(); 
    } 
 
    // shutdown game and release all resources 
    //Game_Shutdown(); 
 
    // show mouse 
    //ShowCursor(TRUE); 
 
    // return to windows like this 
    return (msg.wParam); 


2.  游戲控制流程

[cpp]
int Game_Init(void *parms) 

    // this function is where you do all the initialization for your game 
     
    return 1; 

 
int Game_Shutdown(void *parms) 

    // this function is where you shutdown your game and release all resources  
    // that you allocated 
     
    return 1; 

 
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 you game go here! 
 
    TCHAR buffer[80]; 
 
    // what state is the game in? 
    if (game_state == GAME_STATE_INIT) 
    { 
        // seed the random number generator so game is different each play 
        srand((unsigned int)time(0)); 
 
        // 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() % (WINDOW_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; 
    } 
    else if (game_state == GAME_STATE_START_LEVEL) 
    { 
        // get a new level ready to run 
 
        // initialize the blocks 
        Init_Blocks(); 
 
        // reset block counter 
        blocks_hit = 0; 
 
        // transition to run state 
        game_state = GAME_STATE_RUN; 
    } 
    else if (game_state == GAME_STATE_RUN) 
    { 
        // start the timing clock 
        Start_Clock(); 
 
        // clear drawing surface for next frame of animation 
        Draw_Rectangle(0, 0, WINDOW_WIDTH-1, WINDOW_HEIGHT-1, 0); 
 
        // 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 > (WINDOW_WIDTH - PADDLE_WIDTH)) 
                paddle_x = WINDOW_WIDTH - PADDLE_WIDTH; 
        } 
        else if (KEY_DOWN(VK_LEFT)) 
        { 
            // move paddle to left 
            paddle_x += 8; 
 
            // make sure paddle doesn't go off screen 
            if (paddle_x < 0) 
                paddle_x = 0; 
        } 
 
        // 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 then 
        // bounce it by reflecting its velocity 
        if (ball_x > (WINDOW_WIDTH - BALL_SIZE) || ball_x < 0) 
        { 
            // reflect x-axis velocity 
            ball_dx = -ball_dx; 
 
            // update position 
            ball_x += ball_dx; 
        } 
 
        // now y-axis 
        if (ball_y < 0) 
        { 
            // reflect y-axis velocity 
            ball_dy = -ball_dy; 
 
            // update position 
            ball_y += ball_dy; 
        } 
        else if (ball_y > (WINDOW_HEIGHT - BALL_SIZE)) 
        { 
            // reflect y-axis velocity 
            ball_dy = -ball_dy; 
 
            // update position 
            ball_y += ball_dy; 
 
            // minus the score 
            score -= 100; 
        } 
 
        // next watch out for ball velocity getting out of hand 
        if (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 
        Draw_Rectangle(paddle_x, paddle_y, paddle_x+PADDLE_WIDTH, paddle_y+PADDLE_HEIGHT, PADDLE_COLOR); 
 
 
        // draw the ball 
        Draw_Rectangle(ball_x, ball_y, ball_x+BALL_SIZE, ball_y+BALL_SIZE, 255); 
 
 
        // draw the info 
        wsprintf(buffer, TEXT("FREAKOUT         Score %d                Level %d"), score, level); 
        DrawText_GUI(buffer, 8, WINDOW_HEIGHT-16, 127); 
 
        // sync to 30 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 state 
            game_state = GAME_STATE_SHUTDOWN; 
        } 
    } 
    else if (game_state == GAME_STATE_SHUTDOWN) 
    { 
        // in this state shut everything down and release resources 
 
        // switch to exit state 
        game_state = GAME_STATE_EXIT; 
    } 
 
    return 1; 

3.小球的控制和繪制(原程序為directDraw繪制,此處改為Win32 API繪制)
[cpp] 
/* DRAW FUNCTION **************************************************************************/ 
int Draw_Rectangle(int x1, int y1, int x2, int y2, int color) 

    // this function uses Win32 API to draw a filled rectangle 
    HBRUSH      hbrush; 
    HDC         hdc; 
    RECT        rect; 
 
    SetRect(&rect, x1, y1, x2, y2); 
 
    hbrush = CreateSolidBrush(color); 
    hdc = GetDC(main_window_handle); 
 
    FillRect(hdc, &rect, hbrush); 
    ReleaseDC(main_window_handle, hdc); 
    DeleteObject(hbrush); 
 
    return 1; 

 
int DrawText_GUI(TCHAR *text, int x, int y, int color) 

    HDC hdc; 
 
    hdc = GetDC(main_window_handle); 
 
    // set the colors for the text up 
    SetTextColor(hdc, color); 
 
    // set background mode to transparent so black isn't copied 
    SetBkMode(hdc, TRANSPARENT); 
 
    // draw the text 
    TextOut(hdc, x, y, text, lstrlen(text)); 
 
    // release the dc 
    ReleaseDC(main_window_handle, hdc); 
 
    return 1; 

 
 
/* GAME PROGRAMMING CONSOLE FUNCTIONS *****************************************************/ 
void Init_Blocks(void) 

    // initialize the block field 
    for (int row = 0; row < NUM_BLOCK_ROWS; row++) 
        for (int col = 0; col < NUM_BLOCK_COLUMNS; col++) 
            blocks[row][col] = row*16 + col*3 + 16; 

 
void Draw_Blocks(void) 

    // this function draws all the blocks in row major form 
    int x1 = BLOCK_ORIGIN_X; 
    int y1 = BLOCK_ORIGIN_Y; 
 
    // draw all the blocks 
    for (int row = 0; row < NUM_BLOCK_ROWS; row++) 
    { 
        // reset column position 
        x1 = BLOCK_ORIGIN_X; 
         
        for (int col = 0; col < NUM_BLOCK_COLUMNS; col++) 
        { 
            if (blocks[row][col] != 0) 
            { 
                Draw_Rectangle(x1, y1, x1+BLOCK_WIDTH, y1+BLOCK_HEIGHT, blocks[row][col]); 
            } 
 
            // advance column position 
            x1 += BLOCK_X_GAP; 
        } 
 
        // advance to next row position 
        y1 += BLOCK_Y_GAP; 
    } 

 
void Process_Ball(void) 

    // this function tests if the ball has hit a block or the paddle if so, the ball is bounced  
    // and the block is removed from  the playfield note: very cheesy 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 implement, later we'll see a better way 
 
    // current rendering position 
    int x1 = BLOCK_ORIGIN_X; 
    int y1 = BLOCK_ORIGIN_Y; 
 
    // computer center of ball 
    int ball_cx = ball_x + (BALL_SIZE/2); 
    int ball_cy = ball_y + (BALL_SIZE/2); 
 
    // test the ball has hit the paddle 
    if (ball_y > (WINDOW_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 made contact 
            ball_y += ball_dy; 
 
            // 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)) 
            { 
                game_state = GAME_STATE_START_LEVEL; 
                level++; 
            } 
 
            // make a little noise 
            MessageBeep(MB_OK); 
 
            return; 
        } 
    } 
 
    // now scan thru all the blocks and see of ball hit blocks 
    for (int row = 0; row < NUM_BLOCK_ROWS; row++) 
    { 
        x1 = BLOCK_ORIGIN_X; 
 
        // scan this row of blocks 
        for (int col = 0; col < NUM_BLOCK_COLUMNS; col++) 
        { 
            if (blocks[row][col] != 0) 
            { 
                // test ball against bounding box of block 
                if ((ball_cx > x1) && (ball_cx < x1 + BLOCK_WIDTH) && 
                    (ball_cy > y1) && (ball_cy < y1 + BLOCK_HEIGHT)) 
                { 
                    // remove the block 
                    blocks[row][col] = 0; 
 
                    // increment global block counter, so we know when to start another level up 
                    blocks_hit++; 
 
                    // 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)); 
 
                    return; 
                } 
            } 
 
            // advance column position 
            x1 += BLOCK_X_GAP; 
        } 
 
        // advance row position 
        y1 += BLOCK_Y_GAP; 
    } 

4.時間控制
[cpp] 
/* CLOCK FUNCTIONS ************************************************************************/ 
DWORD Get_Clock(void) 

    // this function returns the current tick count 
 
    // return time 
    return GetTickCount(); 

 
DWORD Start_Clock(void) 

    // this function starts the block, that is, saves the current count, 
    // use in conjunction with Wait_Clock() 
 
    return (start_clock_count = Get_Clock()); 

 
DWORD Wait_Clock(DWORD count) 

    // this function is used to wait for a specific number of clicks since 
    // the call to Start_Clock 
 
    while (Get_Clock() - start_clock_count < count) 
        ; 
 
    return Get_Clock(); 

 

5. 發現的問題

1)顏色太暗:

原因可能是int 到RGB轉換的問題,解決辦法是傳參數時直接用RGB構造。


2)擋板和分數都看不到

原因是原程序沒有標題欄,是全屏顯示的,現在有了標題欄,占了一定的空間,導致位置計算的不對,解決辦法是設置窗口大小時增加邊框和標題欄。


3)界面閃爍及耗費CPU

原因是每次都先繪制黑色屏幕清除全屏,然後重新繪制,較為浪費,且一直在循環,耗費CPU。

解決辦法是減少重繪區域,每次只繪制需要繪制的地方,而不是重繪全屏。

但此次只是為了學習這個框架,所以暫時只是每次循環取消息後睡眠10ms,這只能緩解閃爍和耗費CUP,並不能消除。


完整代碼:

[cpp] 
/*
 * FreakOut.cpp (改編自《Windows 游戲編程大師技巧》第一章示例程序)
  * 2012-11-29
 */ 
 
/* INCLUDES *******************************************************************************/ 
#include <windows.h> 
#include <stdlib.h> 
#include <time.h> 
#include <stdio.h> 
 
 
/* DEFINES ********************************************************************************/ 
// defines for windows 
#define WINDOW_CLASS_NAME   TEXT("WIN32CLASS") 
#define WINDOW_WIDTH        640 
#define WINDOW_HEIGHT       480 
 
// states for game loop 
#define GAME_STATE_INIT         0 
#define GAME_STATE_START_LEVEL  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 
#define BLOCK_COLOR             RGB(125, 125, 0) 
 
#define BALL_COLOR              RGB(222, 222, 222) 
 
// paddle defines 
#define PADDLE_START_X          (WINDOW_WIDTH/2 - 16) 
#define PADDLE_START_Y          (WINDOW_HEIGHT - 32); 
#define PADDLE_WIDTH            32 
#define PADDLE_HEIGHT           8 
#define PADDLE_COLOR            RGB(0, 0, 255) 
 
// ball defines 
#define BALL_START_Y            (WINDOW_HEIGHT/2) 
#define BALL_SIZE                4 
 
// 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) 
 
 
/* basic unsigned types *******************************************************************/ 
typedef unsigned short USHORT; 
typedef unsigned short WORD; 
typedef unsigned char  UCHAR; 
typedef unsigned char  BYTE; 
 
 
/* FUNCTION DECLARATION *******************************************************************/ 
int Game_Init(void *parms = NULL); 
int Game_Shutdown(void *parms = NULL); 
int Game_Main(void *parms = NULL); 
 
DWORD Start_Clock(void); 
DWORD Wait_Clock(DWORD count); 
 
 
/* GLOBALS *********************************************************************************/ 
HWND        main_window_handle  = NULL;             // save the window handle 
HINSTANCE   main_instance       = NULL;             // save the instance 
int         game_state          = GAME_STATE_INIT;  // starting state 
 
int         paddle_x = 0, paddle_y = 0;             // tracks position of paddle 
int         ball_x   = 0, ball_y   = 0;             // tracks position of ball 
int         ball_dx  = 0, ball_dy  = 0;             // velocity of ball 
int         score    = 0;                           // the score 
int         level    = 1;                           // the current level 
int         blocks_hit = 0;                         // tracks number of blocks hit 
 
DWORD       start_clock_count   = 0;                // used for timing 
 
// this contains the game grid data 
UCHAR blocks[NUM_BLOCK_ROWS][NUM_BLOCK_COLUMNS];     
 
 
/* WINDPROC ********************************************************************************/ 
LRESULT CALLBACK WindowProc(HWND    hwnd, 
                            UINT    msg, 
                            WPARAM  wparam, 
                            LPARAM  lparam) 

    // this is the main message handler of the system 
    PAINTSTRUCT ps; 
    HDC         hdc; 
 
    switch (msg) 
    { 
    case WM_CREATE: 
        return 0; 
 
    case WM_PAINT: 
        hdc = BeginPaint(hwnd, &ps); 
        EndPaint(hwnd, &ps); 
        return 0; 
 
    case WM_DESTROY: 
        PostQuitMessage(0); 
        return 0; 
 
    default: 
        break; 
    } 
 
    return DefWindowProc(hwnd, msg, wparam, lparam); 

 
 
/* WINMAIN ********************************************************************************/ 
int WINAPI WinMain(HINSTANCE hinstance, 
                   HINSTANCE hprevinstance, 
                   LPSTR lpcmdline, 
                   int ncmdshow) 

    WNDCLASS    winclass; 
    HWND        hwnd; 
    MSG         msg; 
    HDC         hdc; 
    PAINTSTRUCT ps; 
 
    /* CS_DBLCLKS Specifies that the window should be notified of double clicks with 
     * WM_xBUTTONDBLCLK messages
     * CS_OWNDC標志,屬於此窗口類的窗口實例都有自己的DC(稱為私有DC),私有DC僅屬於該窗口實例,
     * 所以程序只需要調用一次GetDC或BeginPaint獲取DC,系統就為窗口初始化一個DC,並且保存程序
     * 對其進行的改變。ReleaseDC和EndPaint函數不再需要了,因為其他程序無法訪問和改變私有DC。
     */ 
    winclass.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.lpszMenuName   = NULL; 
    winclass.lpszClassName  = WINDOW_CLASS_NAME; 
 
    // register the window class 
    if (!RegisterClass(&winclass)) 
        return 0; 
 
 
    int cxNonClient = GetSystemMetrics(SM_CXBORDER)*2 + 10; 
    int cyNonClient = GetSystemMetrics(SM_CYBORDER) + GetSystemMetrics(SM_CYCAPTION) + 10; 
 
    // Create the window, note the use of WS_POPUP 
    hwnd = CreateWindow( 
        WINDOW_CLASS_NAME,          // class 
        TEXT("WIN32 Game Console"), // title 
        WS_OVERLAPPEDWINDOW,        // style 
        0, 0,                       // initial x, y  
        WINDOW_WIDTH + cxNonClient, // initial width 
        WINDOW_HEIGHT+ cyNonClient, // initial height 
        NULL,                       // handle to parent 
        NULL,                       // handle to menu 
        hinstance,                  // instance 
        NULL);                      // creation parms 
 
    if (! hwnd) 
        return 0; 
 
    ShowWindow(hwnd, ncmdshow); 
    UpdateWindow(hwnd); 
 
    // hide mouse 
    //ShowCursor(FALSE); 
 
    // save the window handle and instance in a global 
    main_window_handle  = hwnd; 
    main_instance       = hinstance; 
 
    // perform all game console specific initialization 
    Game_Init(); 
 
    // enter main event loop 
    while (1) 
    { 
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 
        { 
            // test if this is a quit msg 
            if (msg.message == WM_QUIT) 
                break; 
 
            // translate any accelerator keys 
            TranslateMessage(&msg); 
 
            // send the message to the window proc 
            DispatchMessage(&msg); 
        } 
 
        // main game processing goes here 
        Game_Main(); 
        Sleep(10); 
    } 
 
    // shutdown game and release all resources 
    Game_Shutdown(); 
 
    // show mouse 
    //ShowCursor(TRUE); 
 
    // return to windows like this 
    return (msg.wParam); 

 
 
/* DRAW FUNCTION **************************************************************************/ 
int Draw_Rectangle(int x1, int y1, int x2, int y2, int color) 

    // this function uses Win32 API to draw a filled rectangle 
    HBRUSH      hbrush; 
    HDC         hdc; 
    RECT        rect; 
 
    SetRect(&rect, x1, y1, x2, y2); 
 
    hbrush = CreateSolidBrush(color); 
    hdc = GetDC(main_window_handle); 
 
    FillRect(hdc, &rect, hbrush); 
    ReleaseDC(main_window_handle, hdc); 
    DeleteObject(hbrush); 
 
    return 1; 

 
int DrawText_GUI(TCHAR *text, int x, int y, int color) 

    HDC hdc; 
 
    hdc = GetDC(main_window_handle); 
 
    // set the colors for the text up 
    SetTextColor(hdc, color); 
 
    // set background mode to transparent so black isn't copied 
    SetBkMode(hdc, TRANSPARENT); 
 
    // draw the text 
    TextOut(hdc, x, y, text, lstrlen(text)); 
 
    // release the dc 
    ReleaseDC(main_window_handle, hdc); 
 
    return 1; 

 
 
/* GAME PROGRAMMING CONSOLE FUNCTIONS *****************************************************/ 
void Init_Blocks(void) 

    // initialize the block field 
    for (int row = 0; row < NUM_BLOCK_ROWS; row++) 
        for (int col = 0; col < NUM_BLOCK_COLUMNS; col++) 
            blocks[row][col] = 1; 

 
void Draw_Blocks(void) 

    // this function draws all the blocks in row major form 
    int x1 = BLOCK_ORIGIN_X; 
    int y1 = BLOCK_ORIGIN_Y; 
 
    // draw all the blocks 
    for (int row = 0; row < NUM_BLOCK_ROWS; row++) 
    { 
        // reset column position 
        x1 = BLOCK_ORIGIN_X; 
         
        for (int col = 0; col < NUM_BLOCK_COLUMNS; col++) 
        { 
            if (blocks[row][col] != 0) 
            { 
                Draw_Rectangle(x1, y1, x1+BLOCK_WIDTH, y1+BLOCK_HEIGHT, BLOCK_COLOR); 
            } 
 
            // advance column position 
            x1 += BLOCK_X_GAP; 
        } 
 
        // advance to next row position 
        y1 += BLOCK_Y_GAP; 
    } 

 
void Process_Ball(void) 

    // this function tests if the ball has hit a block or the paddle if so, the ball is bounced  
    // and the block is removed from  the playfield note: very cheesy 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 implement, later we'll see a better way 
 
    // current rendering position 
    int x1 = BLOCK_ORIGIN_X; 
    int y1 = BLOCK_ORIGIN_Y; 
 
    // computer center of ball 
    int ball_cx = ball_x + (BALL_SIZE/2); 
    int ball_cy = ball_y + (BALL_SIZE/2); 
 
    // test the ball has hit the paddle 
    if (ball_y > (WINDOW_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 made contact 
            ball_y += ball_dy; 
 
            // 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)) 
            { 
                game_state = GAME_STATE_START_LEVEL; 
                level++; 
            } 
 
            // make a little noise 
            MessageBeep(MB_OK); 
 
            return; 
        } 
    } 
 
    // now scan thru all the blocks and see of ball hit blocks 
    for (int row = 0; row < NUM_BLOCK_ROWS; row++) 
    { 
        x1 = BLOCK_ORIGIN_X; 
 
        // scan this row of blocks 
        for (int col = 0; col < NUM_BLOCK_COLUMNS; col++) 
        { 
            if (blocks[row][col] != 0) 
            { 
                // test ball against bounding box of block 
                if ((ball_cx > x1) && (ball_cx < x1 + BLOCK_WIDTH) && 
                    (ball_cy > y1) && (ball_cy < y1 + BLOCK_HEIGHT)) 
                { 
                    // remove the block 
                    blocks[row][col] = 0; 
 
                    // increment global block counter, so we know when to start another level up 
                    blocks_hit++; 
 
                    // 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)); 
 
                    return; 
                } 
            } 
 
            // advance column position 
            x1 += BLOCK_X_GAP; 
        } 
 
        // advance row position 
        y1 += BLOCK_Y_GAP; 
    } 

 
int Game_Init(void *parms) 

    // this function is where you do all the initialization for your game 
     
    return 1; 

 
int Game_Shutdown(void *parms) 

    // this function is where you shutdown your game and release all resources  
    // that you allocated 
     
    return 1; 

 
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 you game go here! 
 
    TCHAR buffer[80]; 
 
    // what state is the game in? 
    if (game_state == GAME_STATE_INIT) 
    { 
        // seed the random number generator so game is different each play 
        srand((unsigned int)time(0)); 
 
        // 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() % (WINDOW_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; 
    } 
    else if (game_state == GAME_STATE_START_LEVEL) 
    { 
        // get a new level ready to run 
 
        // initialize the blocks 
        Init_Blocks(); 
 
        // reset block counter 
        blocks_hit = 0; 
 
        // transition to run state 
        game_state = GAME_STATE_RUN; 
    } 
    else if (game_state == GAME_STATE_RUN) 
    { 
        // start the timing clock 
        Start_Clock(); 
 
        // clear drawing surface for next frame of animation 
        Draw_Rectangle(0, 0, WINDOW_WIDTH-1, WINDOW_HEIGHT-1, 0); 
 
        // 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 > (WINDOW_WIDTH - PADDLE_WIDTH)) 
                paddle_x = WINDOW_WIDTH - PADDLE_WIDTH; 
        } 
        else if (KEY_DOWN(VK_LEFT)) 
        { 
            // move paddle to left 
            paddle_x -= 8; 
 
            // make sure paddle doesn't go off screen 
            if (paddle_x < 0) 
                paddle_x = 0; 
        } 
 
        // 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 then 
        // bounce it by reflecting its velocity 
        if (ball_x > (WINDOW_WIDTH - BALL_SIZE) || ball_x < 0) 
        { 
            // reflect x-axis velocity 
            ball_dx = -ball_dx; 
 
            // update position 
            ball_x += ball_dx; 
        } 
 
        // now y-axis 
        if (ball_y < 0) 
        { 
            // reflect y-axis velocity 
            ball_dy = -ball_dy; 
 
            // update position 
            ball_y += ball_dy; 
        } 
        else if (ball_y > (WINDOW_HEIGHT - BALL_SIZE)) 
        { 
            // reflect y-axis velocity 
            ball_dy = -ball_dy; 
 
            // update position 
            ball_y += ball_dy; 
 
            // minus the score 
            score -= 100; 
        } 
 
        // next watch out for ball velocity getting out of hand 
        if (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 
        Draw_Rectangle(paddle_x, paddle_y, paddle_x+PADDLE_WIDTH, paddle_y+PADDLE_HEIGHT, PADDLE_COLOR); 
 
 
        // draw the ball 
        Draw_Rectangle(ball_x, ball_y, ball_x+BALL_SIZE, ball_y+BALL_SIZE, BALL_COLOR); 
 
 
        // draw the info 
        wsprintf(buffer, TEXT("FREAKOUT            Score %d              Level %d"), score, level); 
        DrawText_GUI(buffer, 8, WINDOW_HEIGHT-26, 127); 
 
        // sync to 30 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 state 
            game_state = GAME_STATE_SHUTDOWN; 
        } 
    } 
    else if (game_state == GAME_STATE_SHUTDOWN) 
    { 
        // in this state shut everything down and release resources 
 
        // switch to exit state 
        game_state = GAME_STATE_EXIT; 
    } 
 
    return 1; 

 
 
 
/* CLOCK FUNCTIONS ************************************************************************/ 
DWORD Get_Clock(void) 

    // this function returns the current tick count 
 
    // return time 
    return GetTickCount(); 

 
DWORD Start_Clock(void) 

    // this function starts the block, that is, saves the current count, 
    // use in conjunction with Wait_Clock() 
 
    return (start_clock_count = Get_Clock()); 

 
DWORD Wait_Clock(DWORD count) 

    // this function is used to wait for a specific number of clicks since 
    // the call to Start_Clock 
 
    while (Get_Clock() - start_clock_count < count) 
        ; 
 
    return Get_Clock(); 

 

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved