明輝手游網(wǎng)中心:是一個免費提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺!

通向DirectDraw之捷徑

[摘要]作者:Johnny Watson 譯者:藍(lán)色feel From:www.x-temple.com 這篇文章描述了怎樣通過使用DirectX SDK中的通用庫文件來輕松地建立一個 Direct...
作者:Johnny Watson 譯者:藍(lán)色feel From:www.x-temple.com

    這篇文章描述了怎樣通過使用DirectX SDK中的通用庫文件來輕松地建立一個 DirectDraw對象及其顯示表面(surface)。(這篇文章)對那些想要在不破壞原應(yīng)用程序架構(gòu)的情況下快速掌握它來做些事的人特別有幫助,請注意事實上這些類中抽象了相當(dāng)多且復(fù)雜的事物,因此我強烈地推薦你在掌握它們功能的同時,盡量關(guān)注一下其底層的實現(xiàn),這樣有助于你盡快掌握它們的工作方式。

1.DirectDraw的安裝 

    在本文中,我假定你擁有微軟公司的 Visual C++ , 和 DirectX 8.1 SDK。如果沒有,就快去準(zhǔn)備一份吧。 首先,啟動你的 Visual C++, 創(chuàng)建一個新的 Win32 應(yīng)用程序工程。 然后進(jìn)入 你 DirectX SDK 文件夾中的 multimedia\common\include 目錄 , 拷貝 dxutil.h 和 ddutil.h 至你新建工程的目錄下。 然后將你 DirectX SDK文件夾中的 \multimedia\common\src目錄下的dxutil.cpp 和 ddutil.cpp 也拷貝至你新建工程的目錄下。把四個文件加入你的工程, 然后連接上下列庫文件: dxguid.lib ,ddraw.lib,winmm.lib。現(xiàn)在,你創(chuàng)建一個新的 C++源文件文件, 而且也把它加入你的工程。 這些是整個教程中我們將會用到的工作文件。

2.DirectDraw程序代碼

    既然我們已經(jīng)準(zhǔn)備好了, 讓我們開始寫一些代碼什么的實在的東西吧!



#define WIN32_LEAN_AND_MEAN

#include <windows.h>

#include "dxutil.h"
#include "ddutil.h"


     這是標(biāo)準(zhǔn)的代碼。#define WIN32_LEAN_AND_MEAN,這句的目的是指示編譯器不要包含與MFC相關(guān)的操作。( 只是一個好的練習(xí)——如果你不在使用 MFC) 然后我們包括 dxutil.h 和 ddutil.h,這是兩個很有用的頭文件。 他們能夠使你以一種比通常的DirectX編程更輕松的方式來工作。



//globals

bool g_bActive = false;

CDisplay *g_pDisplay = NULL;
CSurface *g_pText = NULL;


//function prototypes

bool InitDD(HWND);
void CleanUp();
void GameLoop();


    代碼自身說的很清楚,不是嗎?我們的第一個全局變量,g_bActive,是一個讓我們的應(yīng)用程序知道是否應(yīng)該運行游戲的標(biāo)志。 如果我們沒有這個全局變量, 我們的應(yīng)用程序可能在它自己被注銷之後還在嘗試著在我們已經(jīng)創(chuàng)建的 DirectDraw 的表面上畫點兒什么呢!雖然這通常是一個在程序結(jié)束的時候出現(xiàn)的一個不大的問題,但它會導(dǎo)致一個非法操作的錯誤,我們并不想那樣,不是嗎? g_pDisplay 是我們的顯示對象。CDisplay 在 ddutil.h 中是最主要的類。它控制著我們前后的緩沖區(qū),并提供一些針對該緩沖區(qū)的功能,如訪問及存儲緩沖區(qū),將表面寫入緩沖區(qū),創(chuàng)建一個表面,等等。 g_pText 是一個文本表面。 我們將會在這個表面 (你或許已經(jīng)有所理解 ) 之上寫文本, 然后將它傳送到我們的屏幕之上。 注意它們兩者都是指向?qū)ο蟮闹羔? 而且被初始化為NULL。
     現(xiàn)在來看看函數(shù)的原型。 InitDD() 只是用來初始化 DirectDraw 。 多虧了 DirectDraw 的通用文件(common files),這還算是一個簡單的程序。( 但是晚些時候我們才會接觸到它們) CleanUp()調(diào)用了對象 g_pDisplay 的析構(gòu)函數(shù)來釋放 ( release)我們所創(chuàng)建的DirectDraw對象及其表面。很明顯, GameLoop() 則是將來用來放你的游戲的地方。


    LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg,
                                WPARAM wParam, LPARAM lParam)
    {
        switch(uMsg)
        {
            case WM_CREATE:
                InitDD(hWnd);
                g_bActive=true;
                break;
            case WM_CLOSE:
                g_bActive=false;
                CleanUp();
                DestroyWindow(hWnd);
                break;
            case WM_DESTROY:
                PostQuitMessage(0);
                break;
            case WM_MOVE:
                g_pDisplay->UpdateBounds();
                break;
            case WM_SIZE:
                g_pDisplay->UpdateBounds();
                break;
            default:
                return DefWindowProc(hWnd,uMsg,wParam,lParam);
                break;
        }
        return 0;
    }


     這里是標(biāo)準(zhǔn)的 Windows 程序消息處理機制的代碼。 當(dāng)產(chǎn)生 WM_CREATE 消息時,我們初始化 DirectDraw, 并將我們的全局變量 g_bActive 設(shè)定為true,這樣就可以執(zhí)行游戲循環(huán) GameLoop() 了。當(dāng)消息 WM_CLOSE 被傳遞的時候,我們就把全局變量 g_bActive 設(shè)定為 false ( 這樣我們的應(yīng)用程序就不會在它自己被注銷之後還在嘗試著向我們已經(jīng)創(chuàng)建的 DirectDraw 的表面上寫數(shù)據(jù)了)。然后調(diào)用 CleanUp() 函數(shù),最終注銷我們的窗口程序。處理 WM_MOVE 和 WM_SIZE 事件非常重要,因為若處理不當(dāng),DirectDraw 將不顧窗口本身是否已經(jīng)被移動或重新設(shè)定其大小,仍然在屏幕上原來的位置上繼續(xù)作畫,從而造成錯誤。

    bool InitDD(HWND hWnd)
    {
        //dd init code
        g_pDisplay = new CDisplay();

        if(FAILED(g_pDisplay->CreateWindowedDisplay(hWnd,640,480)))
        {
            MessageBox(NULL,"Failed to Initialize DirectDraw",
                        "DirectDraw Initialization
            Failure",MB_OK MB_ICONERROR);
            return false;
        }
        return true;
    }


     該死的 InitDD() 函數(shù)... 不過別急,它只不過幾行而已! 這兒就是通常那些個庫文件的用武之地。現(xiàn)在那些妨礙我們建立 DirectDraw 對象的冗長的玩意兒已被我們輕松搞定了,而你將會再次注意到它挺麻煩的,不是嗎?如果你真的不想弄明白那些惹人煩的玩意兒,那么至少你得知道個梗概吧!如果你得回去改變協(xié)作等級什么的,它也許幫得上忙。注意到這是一個返回 bool 值的函數(shù), 因此如果你不厭倦的話, 你應(yīng)當(dāng)做一下錯誤檢查。(基于你所可以理解的篇幅問題之原因,我決定在這篇文章中省略)

    void CleanUp()
    {
        SAFE_DELETE(g_pDisplay);
    }


     真是夠簡單的! 這個函數(shù)調(diào)用了在 dxutil.h 中定義的宏 SAFE_DELETE 來刪除我們的顯示對象,同時調(diào)用析構(gòu)函數(shù)。

    void MainLoop()
    {
        g_pDisplay->CreateSurfaceFromText(&g_pText,NULL,"DDraw using Common Files",
        RGB(0,0,0),RGB(0,255,0));

        g_pDisplay->Clear(0);
        g_pDisplay->Blt(0,0,g_pText,0);
        g_pDisplay->Present();

        g_pText->Destroy();
    }


     這兒就是你將來放游戲的地方。 為了要給你一個表面對象(surface object)如何工作的例子,我們已經(jīng)做了一個簡單的文本表面而且寫了一些文本上去。注意我們在最後釋放(destroy)了全局指針變量 g_pText,否則每次循環(huán)它都將被重新創(chuàng)建一次,直到吃光最后一點兒內(nèi)存。

    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                        LPSTR lpCmdLine, int iShowCmd)
    {
        WNDCLASSEX wc;
        HWND hWnd;
        MSG lpMsg;

        wc.cbClsExtra=0;
        wc.cbSize=sizeof(WNDCLASSEX);
        wc.cbWndExtra=0;
        wc.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH);
        wc.hCursor=LoadCursor(NULL,IDC_ARROW);
        wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);
        wc.hIconSm=LoadIcon(NULL,IDI_APPLICATION);
        wc.hInstance=hInstance;
        wc.lpfnWndProc=WndProc;
        wc.lpszClassName="wc";
        wc.lpszMenuName=0;
        wc.style=CS_HREDRAW CS_VREDRAW;

        if(!RegisterClassEx(&wc))
        {
            MessageBox(NULL,"Couldn't Register Window Class",
                        "Window Class Registration
                        Failure",MB_OK MB_ICONERROR);
            return 0;
        }

        hWnd = CreateWindowEx(NULL,"wc","DirectDraw Common Files in Action",
                                WS_POPUPWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,640,480,0,0,
                                hInstance,0);
        if(hWnd == NULL)
        {
            MessageBox(NULL,"Failed to Create Window","Window Creation
                        Failure", MB_OK MB_ICONERROR);
            return 0;
        }

        ShowWindow(hWnd,SW_SHOW);
        UpdateWindow(hWnd);

        while(lpMsg.message != WM_QUIT)
        {
            if(PeekMessage(&lpMsg,0,0,0,PM_REMOVE))
            {
                TranslateMessage(&lpMsg);
                DispatchMessage(&lpMsg);
            }
            else if(g_bActive)
            {
                MainLoop();
            }
        }
        return lpMsg.wParam;
    }


     這就是我們的應(yīng)用程序中最長的函數(shù)——WinMain()。像往常一樣, 我們創(chuàng)建一個窗口類,然后再創(chuàng)建一個窗口,顯示及更新它, 并且進(jìn)入消息循環(huán)?瓷先ブ餮h(huán)不同於平常是因為我們不想要主游戲進(jìn)程被消息的處理所干擾。我們通過觀察全局指針變量 g_bActive 的狀態(tài) 來判斷該時刻調(diào)用游戲循環(huán)是否安全以及在循環(huán)中向屏幕上傳送(blit)圖象,而最后全部結(jié)束的時候我們返回一個長指針 lpMsg.wParam.( 至于為什么我實在是不敢肯定,可其它每個 Win32 應(yīng)用程序都是這樣做的,對,就是這樣)
     實在是太簡單了,嗯!?我們只用了 135 行程序編碼就已經(jīng)將對象寫到了屏幕上。自由地去進(jìn)一步探究這些類的結(jié)構(gòu),做一些載入位圖至表面一類的實驗等等。這是使用 DDraw的一個很棒的捷徑。它能在不犧牲控制 (你總是能在你需要的時候回去編輯那些類) 和性能的情況下而使事情變得容易些。有一件事得要注意的是在計算機上如果我使用了這個結(jié)構(gòu)卻不繪制任何對象到屏幕上, 應(yīng)用程序?qū)i定。( 對,這就是我為什麼把文本輸出也包括在這里) 如果我說做游戲很大程度上就是把對象傳送( blit )到屏幕上,大概不會引起什么爭議(除非又有一些新的藝術(shù)風(fēng)格誕生了)。


標(biāo)簽:通向DirectDraw之捷徑