用MFC构造DirectX应用框架 摘要:把DirectX SDK封装在MFC的类中,建立一个DirectX应用程序框架。 关键词:MFC类库 DirectX SDK 一、MFC类库与DirectX SDK Microsoft DirectX SDK是开发基于Windows平台游戏的一个软件开发工具,其 主要功能主要包括在五个组件中:DirectDraw、DirectSound、DirectPlay、Direct3D和DirectInput,每个组件都具不同的功能: l DirectDraw使用直接写存技术加快游戏的动画速度; l DirectSound控制游戏声音的合成和播放; l DirectPlay使游戏具有网络多人游戏功能; l Direct3D让程序员更方便地开发三维游戏; l DirectInput使游戏支持更多的输入设备(现在只支持游戏杆、鼠标和键盘)。 可以说DirectX SDK提供了编写一个游戏所必须的功能及基层函数,所以大多 Windows游戏都使用了DirectX SDK。 MFC(Microsoft Foundation Class)类库是Microsoft Visual C++中提供的一个 功能强大的Windows应用程序开发类,使用这些类我们可以避免和繁琐的Windows API 打交道,而且在Visual C++中我们还可以利用ClassWizard对MFC类进行Windows消息映 射,所以如果能用MFC类库来开发DirectX SDK的应用程序,至少有以下几个好处: 1. 可以用VC++的ClassWizard方便地对Window消息进行映射; 2. 增加了程序的可读性,并且可以用VC++的ClassView方便的管理所用的类; 3. 增加程序代码的可重用性,可以在原有的基础上开发出功能更强大的应用程序; 更进一步,如果我们能开发出一个能生成DirectX SDK应用程序基本框架的 VC++的工程向导,则为以后开发DirectX SDK应用程序提供及大的方便。下面,我们将 用Visual C++5.0先编写一个DirectX SDK应用程序的基本框架。 二、编写DirectX SDK应用程序基本框架 我们按下列步骤建立一个DirectX SDK程序的基本框架: 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类,这是我们的DirectX应用程序的基类。CDirectXWnd类 创建一个窗口并生成一个与该窗口相关联的DirectDraw对象lpDD,同时还生成一个显 示平面(lpFrontBuffer)和一个显示缓冲平面(lpBackBuffer),该类使用了几个 虚函数,必要时其派生类可以覆盖这些函数。 4. 打开DirectX.CPP,把#include “DirectXDlg.h”改为#include “ DirectXWnd.H”然后把CDirectXApp::InitInstance()函数修改如下,其中粗体字为 要增加的内容: BOOL CDirectXApp::InitInstance() { #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->bIsActive ) { pWnd->UpdateFrame(); } } } return TRUE ; } 编译该程序并运行,可以看到出现一个黑色的屏幕窗口,按ESC或F12则可退出程序, 至此,我们的基本框架已经建立好了,虽然这个框架还比较简单,但我们可以在此基 础上开发出更强大的应用框架。为了方便使用该框架,我们可以为该框架写一个 Custom AppWizard,当然也可以不写,只要把该工程目录下的文件拷贝到另一个工程 目录中即可。 三、测试框架 现在,我们按下列步骤写一个测试程序来测试这个框架: 1、把刚才创建的工程框架拷贝到一个新目录下,并打开。用ClassView创建一个 基于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 =0x%08lX", 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; } } } 4、编译并运行该程序,屏幕上会出现一个白色球在移动。 附录: 文件DirectXWnd.H #if !defined(DIRECTXWND_H) #define DIRECTXWND_H // DirectXWnd.h : header file #include #pragma comment (lib,"ddraw.lib") //链接时加入ddraw.lib库 class CDirectXWnd : public CWnd { // Construction public: CDirectXWnd(); // Attributes public: BOOL bIsActive; //应用程序是否激活 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 // !defined(DIRECTXWND_H) 文件DirectXWnd.CPP // DirectXWnd.cpp : implementation file #include "stdafx.h" #include "DirectX.h" #include "DirectXWnd.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif CDirectXWnd::CDirectXWnd() { lpDD = NULL ; lpFrontBuffer=NULL; lpBackBuffer=NULL; nBufferCount = 0 ; bIsActive = 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 ; } 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) 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); bIsActive = bActive ; }