用MFC构造DirectX应用框架

    技术2022-05-11  66

    作者:广西职业技术学院电算中心(530227)谭文洪


    摘  要:把DirectXSDK封装在MFC的类中,建立一个DirectX应用程序框架。 关键词:MFC类库,DirectXSDK

    一、 MFC类库与DirectXSDK

    Microsoft DirectX SDK是开发基于 Windows平台游戏的一个软件开发工具,其主要功能主要包括在五个组件中: DirectDraw、 DirectSound、DirectPlay、Direct3D和DirectInput,每个组件都具不同的功能:

    .DirectDraw使用直接写存技术加快游戏的动画速度; .DirecSound控制游戏声音的合成和播放; .DirectPlay使游戏具有网络多人游戏功能; .Direct3D让程序员更方便地开发三维游戏: .DirectInput使游戏支待更多的输入设备(现在只支持游戏杆、鼠标和键盘)。

    可以说DirectXSDK提供了编写一个游戏所必须的功能及基层函数,所以大多Windows游戏都使用了DitrectXSDK。

    MFC(Microsoft Foundation Class)类库是Microsoft Visual C++中提供的一个功能强大的Windows应用程序开发类,使用这些类我们可以避免和繁琐的Windows APl打交道,而且在VisualC++中我们还可以利用C1assWizard对MFC类进行Windows消息映射,所以如果能用MFC类库来开发DirectXSDK的应用程序,至少有以下几个好处:

    1.可以用 VC++的 C1assWizard方便地对Windows消息进行映射; 2.增加了程序的可读性,并且可以用VC++的ClassView方便的管理所用的类; 3.增加程序代码的可重用性,可以在原有的基础上开发出功能更强大的应用程序更进一步,如果我们能开发出一个能生成DirectXSDK应用程序基本框架的VC++的工程向导,则为以后开发DirectX SDK应用程序提供及大的方便。下面,我们将用Visua1 C++5.0先编写一个DirectXSDK应用程序的基本框架。

    二、编写DirectXSDK应用程序基本框架

    我们按下列步骤建立一个勘rectXSDK程序的基本框架:

    1.用 Visual C++的 MFC AppWizard(EXE)生成一个基于对话框的工程文件,取名为DirectX,在向导第二步时取消About Box的复选框,然后按Finish按钮。 2.删除在DirectX工程目录中生成的DirectXDlg.CPP和DirectXDlg.H两个文件,并在Visual C++的FileView中删除以上两个文件,按CTRL十W启动ClassWizard删除CDirectXDlg类,然后在ResourceView中删除 IDD_DIRECTX_ DIALOG。 3.建立两个文件 DirectXWnd.CPP和DirectXWnd.H(这两个文件在本文的附录中,请注意不要删除有“//{”和“//}”之间的内容,否则将不能使用ClassWizard对窗口消息进行映射),并把它们加入到工程中。这时工程中将增加一个基于CWnd的CDirectXWnd类,这是我们的DirrectX应用程序的基类。CDirectXWnd类创建一个窗口并生成一个与该窗口相关联的DirectDraw对象lpDD,同时还生成一个显示平面(lpFrontBuffer)和一个显示缓冲平面(lpBackBuffer),该类使用了几个虚函数,必要时其派生类可以覆盖这些函数。 4.打开DirectX.CPP,把# include“DirectXDlg.h”改为#include“DirectXWnd.H”然后把CDirectXApp::InitInstance()函数修改如下,其中黑体字为要增加的内容:

    BOOL CDirectXApp::lnitlnstnnce() {     #ifdef _AFXDLL    Enable3dControls();//Call this when Using MFC in a shared DLL     #else    Enable3dControlsStatic();//Call this when linking to MFC statically     #endif

        CDirectXWnd *pWnd=new CDirectXWnd();     pWnd->Create("DirectXWnd Test");     m_pMainWnd = pWnd;     pWnd->UpdateWindow();     pWnd->SetFocus();     if(pWnd->InitializeGame(640,480,8)==FALSE)     {         pWnd->DestroyWindow();         return FALSE;     }     MSG msg;     while(1)     {         if(PeekMessage(&msg,NULL,0,0,PM_NOREMOVE))         {             if(!GetMessage(&msg,NULL,0,0))                 return msg.wParam;             TranslateMessage(&msg);             DispatchMessage(&msg);         }         else         {             if(pWnd->blsActive)             {                 pWnd->UpdateFrame();             }         }     }     return FALSE; } 编译该程序并运行,可以看到出现一个黑色的屏幕窗口,按ESC或F12则可退出程序。至此,我们的基本框架已经建立好了,虽然这个框架还比较简单,但我们可以在此基础上开发出更强大的应用框架。为了方便使用该框架,我们可以为该框架写一个Custom AppWizard,当然也可以不写,只要把该工程目录下的文件拷贝到另一个工程目录中即可。

    三、测试框架

    现在,我们按下列步骤写一个测试程序来测试这个框架:

    1.把刚才创建的工程框架拷贝到一个新目录下,并打开。用C1assView创建一个基于CWnd的类CTestWnd,然后把CTestWnd.h和CTestWnd.CPP文件中的所有"CWnd"字符串替换为"CDirectXWnd",并在CTestWnd.h文件头加入下列 字符串:#include"DirectXWnd.h"。 2.打开DirectX.CPP文件,在文件头加入#include"TestWnd.h",并把该文件中的所有"CDirectXWnd"字符串替换成"CTestWnd"并保存。 3.为 CTestWnd类增加一个虚函数UpdateFrame(),这个函数覆盖了其基类CDirectWnd的UpdateFrame():

    void CTestWnd::UpdateFrame() {     static int x=0,dx=5;     static int y=0,dy=5;     HDC hdc;     DDBLTFX ddbltfx;     HRESULT ddrval;     UpdateWindow();     ddbltfx.dwSize=sizeof(ddbltfx);     ddbltfx.dwFillColor=0;     ddrval=lpBackBuffer->Blt(         NULL,//dest rect         NULL,//src surface         NULL,//src rect         DDBLT_COLORFILL|DDBLT_WAIT,         &ddbltfx);     if(ddrval!=DD_OK)     {         Msg("Fill failed ddrval=0x1x",ddrval);         return;     }     if(lpBackBuffer->GetDC(&hdc)==DD_OK)     {         if(x<0)dx=5;         if(x>590)dx=-5;         if(y<0)dy=5;         if(y>430)dy=-5;         x+=dx;y+=dy;         Ellipse(hdc,x,y,x+50,y+50);         lpBackBuffer->ReleaseDC(hdc);     }     while(1)     {         HRESULT ddrval;         ddrval=lpFrontBuffer->Flip(NULL,0);         if(ddrval==DD_OK)         {             break;         };         if(ddrval==DDERR_SURFACELOST)         {             if(!CDirectXWnd::RestoreSurfaces())             {                 break;             }         }         if(ddrval!=DDERR_WASSTILLDRAWING)         {             break;         }     } }

    无心柳按:光是这么追加在测试中没有成功,可能要在CTestWnd.H中追加两行: public:     void    UpdateFrame(); 否则编译可能出错。

    4、编译并运行该程序,屏幕上会出现一个白色球在移动。

    附录:

    文件:DirectXWnd.H

    #if!defined(DIRECTXWND_H) #define DIRECTXWND_H //DirectXWnd.h:header file #include<ddraw.h> #pragma comment(lib,"ddraw.lib")//链接时加入ddraw.lib库 class CDirectXWnd:public CWnd { //Construction public:     CDirectXWnd();     //Attributes public:     BOOL blsActive;//应用程序是否被激活 protected:     LPDIRECTDRAW lpDD;//DirectDraw对象指针     LPDIRECTDRAWSURFACE lpFrontBuffer;//DirectDraw主缓冲区     LPDIRECTDRAWSURFACE lpBackBuffer;//DirectDraw后台缓冲区     int nBufferCount;//后备缓冲区个数     //Operations protected:     void Msg(LPSTR fmt,...); public:     BOOL Create(LPCSTR lpszAppName);//创建窗体     //Overrides     virtual BOOL InitializeGame(UINT GModex,UINT GModeY,UINT GBPP);     virtual BOOL CleanSurface();     virtual void UpdateFrame();     virtual BOOL RestoreSurfaces(void);     //{{AFX_VIRTUAL(CDirectXWnd)     //}}AFX_VIRTUAL     //implementation public:     virtual ~CDirectXWnd();     //Generated message map functions protected:     //{{AFX_MSG(CDirectXWnd)     afx_msg void OnKeyDown(UINT nChar,UINT nRepCnt,UINT nFlags);     afx_msg void OnActivateApp(BOOL bActive,HTASK hTask);     //}}AFX_MSG     DECLARE_MESSAGE_MAP() }; {{AFX_INSERT_LOCATION}} #endif//!define(DIRECTXWND_H)

    文件:DirectXWnd.CPP //DirectXWnd.cpp:implementation file #include "stdafx.h" #include "DirectX.h" #include "DirectXWnd.h" #ifdef _DUBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[]=__FILE__; #endif CDirectXWnd::CDirectXWnd() {     lpDD=NULL;     lpFrontBuffer=NULL;     lpBackBuffer=NULL;     nBufferCount=0;     blsActive=TRUE; } CDirectXWnd::~CDirectXWnd() {     if(lpDD)     {         CleanSurface();         lpDD->Release();         lpDD=NULL;     } } BOOL CDirectXWnd::Create(LPCSTR lpszAppName) {     CString className=AfxRegisterWndClass(CS_DBLCLKS,::LoadCursor(NULL,IDC_ARROW),         NULL,AfxGetApp()->LoadIcon(IDR_MAINFRAME));     return(CWnd::CreateEx(WS_EX_APPWINDOW,className,lpszAppName,         WS_VISIBLE|WS_SYSMENU|WS_POPUP,0,0,GetSystemMetrics(SM_CXSCREEN),         GetSystemMetrics(SM_CYSCREEN),NULL,NULL)); } BOOL CDirectXWnd::InitializeGame(UINT GModeX,UINT GModeY,UINT GBPP) {     HRESULT ddrval;     ddrval=DirectDrawCreate(NULL,&lpDD,NULL);     if(ddrval!=DD_OK)     {         Msg("DirectDrawCreate failed err=%d",ddrval);         return FALSE;     }     ddrval=lpDD->SetCooperativeLevel(m_hWnd,DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN);     if(ddrval!=DD_OK)     {         Msg("SetCooperativeLevel failed err=%d",ddrval);         return FALSE;     }     ddrval=lpDD->SetDisplayMode(GModeX,GModeY,GBPP);     if(ddrval!=DD_OK)     {         Msg("SetDisplayMode failed err=%d",ddrval);         return FALSE;     }     //check capabilites     DDCAPS ddcaps;     ddcaps.dwSize=sizeof(ddcaps);     ddrval=lpDD->GetCaps(&ddcaps,NULL);     if(ddrval!=DD_OK)     {         Msg("GetCaps failed err=%d",ddrval);         return FALSE;     }     if(ddcaps.dwCaps&DDCAPS_NOHARDWARE)     {         Msg("No hardware support at all");     }     //default to double buffered on 1mb,triple buffered     if(nBufferCount==0)     {         if(ddcaps.dwVidMemTotal<=1024L*1024L*(GBPP/8)||GModeX>640)         {             nBufferCount=2;         }         else         {             nBufferCount=3;         }     }     DDSURFACEDESC ddsd;     ::ZeroMemory(&ddsd,sizeof(ddsd));     ddsd.dwSize=sizeof(ddsd);     ddsd.dwFlags=DDSD_CAPS|DDSD_BACKBUFFERCOUNT;     ddsd.ddsCaps.dwCaps=DDSCAPS_PRIMARYSURFACE|DDSCAPS_FLIP|DDSCAPS_COMPLEX;     ddsd.dwBackBufferCount=nBufferCount-1;     ddrval=lpDD->CreateSurface(&ddsd,&lpFrontBuffer,NULL);     if(ddrval!=DD_OK)     {         Msg("CreateSurface failed err=%d",ddrval);         return FALSE;     }     else Msg("显示内存允许建立的缓冲区数=%d(最多需要3)",nBufferCount);     DDSCAPS ddscaps;     ddscaps.dwCaps=DDSCAPS_BACKBUFFER;     ddrval=lpFrontBuffer->GetAttachedSurface(&ddscaps,&lpBackBuffer);     if(ddrval!=DD_OK)     {         Msg("GetAttachedsurface failed err=%d",ddrval);         return FALSE;     }     return TRUE; } void CDirectXWnd::Msg(LPSTR fmt,...) {     char buff[256];     va_list va;     lstrcpy(buff,"DirectxWnd:");     va_start(va,fmt);     wvsprintf(&buff[lstrlen(buff)],fmt,va);     va_end(va);     lstrcat(buff,"/r/n");     AfxMessageBox(buff); } Virtual Function BOOL CDirectXWnd::RestoreSurfaces() {     HRESULT ddrval;     ddrval=lpFrontBuffer->Restore();     if(ddrval!=DD_OK)         return FALSE;     return TRUE; } BOOL CDirectXWnd::CleanSurface() {     if(lpBackBuffer)     {         lpBackBuffer->Release();         lpBackBuffer=NULL;     }     if(lpFrontBuffer)     {         lpFrontBuffer->Release();         lpFrontBuffer=NULL;     }     return TRUE; } void CDirectXWnd::UpdateFrame() { } BEGIN_MESSAGE_MAP(CDirectXWnd,CWnd) //{{AFX_MSG_MAP(CDirectXWnd,Cwnd) ON_WM_KEYDOWN() ON_WM_ACTIVATEAPP() //}}AFX_MSG_MAP END_MESSAGE_MAP() ///CDirectXWnd message handlers void CDirectXWnd::OnKeyDown(UINT nChar,UINT nRepCnt,UINT nFlags) {     switch(nChar)     {     case VK_ESCAPE:     case VK_F12:         PostMessage(WM_CLOSE);         break;     }     CWnd::OnKeyDown(nChar,nRepCnt,nFlags); } void CDirectXWnd::OnActivateApp(BOOL bActive,HTASK hTask) {     CWnd::OnActivateApp(bActive,hTask);     blsActive=bActive;     return; }


    请下载:1、DirectX.zip,工程框架源文件;2、CTestWnd.zip,测试程序源文件。 使用这两个源程序时不要直接编译,因为没有包含编译需要的所有文件,而应该根据本文提示做,需要输入代码的时候,将该文件替换成下载包中的相应文件就可以。另外,请在运行时不要用ALT+TAB切换出来,否则会被锁住,非Reset不可。要想不被锁住,请深入研究DirectDraw技术修改此程序。----无心柳

    本文摘自《计算机编程技巧与维护》杂志1999年第3期 整理:无心柳(全部代码已经输入测试通过)1999/11/02


    最新回复(0)