* 다이렉트드로우내에윈도우대화상자사용하기! 안녕하세요! 라디안소프트김동철입니다! 이번문서강연은다이렉트드로우 7.0 SDK에서 ddraw 예제로포함되었는 FSWindow에대해서정리를해보도록하겠습니다. FSWindow 예제는다이렉트드로우모드내에서윈도우대화상자을띄워서사용할수있는방법을알려준예제였습니다. 전개인적으로이방식이무척이나마음에들었습니다. 게을러서그런지몰라서^_^;;, 맵이나스프라이트툴을제작하기위해서 GDI표면에새로출력을하는방식의윈도우프로그램을제작하기싫어서였다고생각됩니다. 전 MFC에약하기때문에, 대부분윈도우 API를이용해서프로그램을코딩합니다.! 그전엔맵툴이나스프라이트툴을만들기위해서다이렉트드로우기반에서모든인터페이스를만들어서 ( 그려서 ) 사용을했었습니다. 아직도그툴을개선해서사용하고있습니다. ㅎㅎ하지만, 어떤기능을추가하고자할때마다인터페이스를새로만들고, 좌표를잡아주고기능을하게만드는단순작업이무척이나많았습니다. 하지만 FSWindow의예제처럼일반다이렉트드로우환경에서윈도우대화상자를바로이용할수있다는것은단순하고시간이많이걸리는인터페이스작업을빠르게할수있게해주었습니다. 전이방식을이용해서스프라이트툴이나맵툴, 간이이펙트, 기타편집툴을을만들어서사용합니다. 이방식이좋은점은최종완성된게임에서보이는화면에서동일한화면에서스프라이트, 맵작업을하기때문에게임의완성되었을때의최종모습을보면서작업을하는것과같아서, 이미지나맵의문제점을바로찾을수가있다는것이죠, 그리고, 스프라트에디터나, 맵에디터에서사용한그래픽출력엔진코드를그대로메인게임소스쪽으로가져다쓸수있기때문에제작시간을단축할수있습니다. 복잡한기능들을다가지고있는범용툴보다특정프로그램에특화된툴을빨리만드는데는무척이나도움이됩니다. 이프로그래밍방식역시한번사용하고나서, 나중에다시제작을하려면또다시전체를들여다보는번거로움이저에게있었습니다. 그래서이번기회에정리한다는생각에서자료를준비했습니다. 전번 KGDA 강연에서는게임용인스톨쉴드의작업하기였는데, 그역시지금의이유와동일한목적에서제작한것으로기억이남니다. 벌써 1년이지나갔군요, 개인적으로는별로한일없이시간만흐른것같습니다. 먼저 FSWindow의예제를불필요한공의에니메이션은제거하고필요한다이얼로그의이용법과어떻게내프로그램에가져다사용할것인가대해서다루어보도록하겠습니다. 또한보너스로현재예제로제공되는소스는디버깅모드에서는창모드로, 리테일모드에서는풀화면으로실행되도록코드내에작업해놓았습니다. 이렇게해놓으니디버깅이쉬워지는군요, 그전까지디버깅은오로지눈치와직감으로그리고메시지상자를이용한노가다, 화면에메시지출력방식으로해왔는데, 이렇게하니컴파일러의디버깅기능에도움을많이받게되는군요.. ㅎㅎ 사용하는방법은다이렉트드로우예제에있는 fswindow.h fswindow.cpp를프로젝트에포함시키는것을먼저해야합니다.
그다음은초기화하고, 사용하면됩니다. 먼저메인프로그램헤더에다음변수들을선언해줍니다. #include fswindow.h //*************************************************************************************** // 아래변수들은대화상자를컨트롤하는변수입니다. HWND g_hwndhelp = NULL; HWND g_hwnd_workdlg = NULL; // 작업메뉴다이얼박스 // 대화상자내에입력을처리하는콜백함수 ^_^;; LRESULT CALLBACK Dial_MainMenu_Proc(HWND hdlg, UINT umsg, WPARAM wparam, LPARAM lparam); //*************************************************************************************** 다른이름으로변수선언을해도상관없지만, 귀찮아서그냥가져다가사용합니다 ^_^;; 선언이끝났으면, 다이렉트드로우초기화를한이후에대화상자를처리해주는부분을초기화합니다. //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // 다이렉트드로우화면에대화상자사용을위한초기화작업 // 아래함수호출을해서대화상자를컨트롤해줄부분을초기화해줌니다. FSWindow_Init(hWnd, lpdd7, lpddsprimary, lpddsback); //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> fswindow 를초기화한후에대화상자를생성해줍니다. //*************************************************************************************** // 다이얼박스를생성한다. g_hwnd_workdlg = CreateDialog(g_hInstance, MAKEINTRESOURCE(IDD_MAINMENU), hwnd, (DLGPROC) Dial_MainMenu_Proc); // 창이위치를변경한다. SetWindowPos(g_hWnd_WorkDlg,HWND_TOP,g_rcWindow.left+550,g_rcWindow.top+10,0,0,SWP_NOSIZE); ShowWindow(g_hWnd_WorkDlg, SW_SHOWNORMAL); //*************************************************************************************** 대화상자를생성하고, 위치는화면중앙에서오른쪽으로이동시켜놓습니다. 인터페이스이니까, 화면가운데떵하고띄워지면, 뻘쭘하겠죠 //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // fswindow 시작, 아래함수가실행되면다이렉트드로우화면상에대화상자가나타납니다. g_hwndhelp = FSWindow_Begin(g_hWnd_WorkDlg, FALSE); //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
위의과정이처음다이렉트드로우표면상에대화상자를만드는작업입니다. 하지만, 다음에해야할부분이중요합니다. 아래설명한부분까지작업을해야정상적으로대화상자를볼수가있습니다. while( 1 ) if( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ) if(!getmessage( &msg, NULL, 0, 0 ) ) return msg.wparam; //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // 대화상자가보여지면, 모달리스대화상자를위해서메시지를교환시킨다. if (g_hwnd_workdlg == (HWND )NULL!IsDialogMessage(g_hWnd_WorkDlg, &msg)) // 메시지의번역과패치를하는곳 TranslateMessage(&msg); DispatchMessage(&msg); //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> else if(blsactive) DDUpdateFrame(); // 화면업데이트. else WaitMessage(); // 메지지없으면기다리기. 위의코드를보면메인프로그램코드의어느부분인지아실겁니다. 내응용프로그램에서메시지를받고처리하는메인코드부분이지죠, 다이렉트드로우내에나타나게한대화상자를위한메시지처리를해주는부분입니다. 위처럼라인 > 이그려져있는안쪽부분을가져다사용하시면되겠습니다. 다음을실제화면출력을해주를함수내부에다음과같이코드를수정합니다.
void DDUpdateFrame() HRESULT ddrval; // 작업표면에배경이미지출력 lpddswork->bltfast(0,0,lpddsbmp,&g_rcscreen,false); // 대부분의그래픽작업은여기서다수행한다.( 맵, 캐릭터, 이펙트, 기타등등 ) // 2차표면에작업표면을출력한다. lpddsback->bltfast(0,0,lpddswork,&g_rcscreen,false); DisplayFrameRate(); // 현재프레임수를출력한다. // 윈도우창과함께사용할경우에는다음과같이출력부를맞추어주어야한다. if (FSWindow_IsActive()) FSWindow_Update(); else // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // 대화상자를사용하는경우에는 Blt 방식만을이용한다. // 아니면, 화면이그냥죽어버릴것임 ^_^;; ddrval = lpddsprimary->blt(&g_rcwindow, lpddsback, NULL, DDBLT_WAIT, NULL); /* // 대화상자를사용하지않은경우는아래코드로바뀌어준다. #ifdef _DEBUG ddrval = lpddsprimary->blt(&g_rcwindow, lpddsback, NULL, DDBLT_WAIT, NULL); #else ddrval = lpddsprimary->bltfast(0,0,lpddsback,&g_rcwindow,ddbltfast_nocolorkey); if(ddrval == DD_OK) return; #endif */ // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> if(ddrval == DDERR_SURFACELOST) // 화면을잃어버린경우에대한처리 ddrval = lpddsprimary->restore(); if(ddrval!= DD_OK)
MessageBox(hWnd,"lpDDSPrimary->Restore() failed!!!",null,mb_ok); return; //end FSWindow //end ddupdateframe 자, 위해서살펴본부분만수정하면문제없이다이렉트드로우환경에서윈도우대화상자를자유자재로사용할수가있습니다. 풀소스에서는기본적을대화상자를생성하는것과제거하는것, 그리고다시만드는것을담고있습니다. 여러개의대화상자형태를미리만들어놓고계층적으로필요한대화상자를불러서사용하게되면, 원하는모든기능을추가한에디터처럼발전시킬수가있습니다. 전여러개의대화상자를메뉴처럼로드해서, 맵툴, 스프라이툴을제작해서사용하고있습니다. 작업결과는바로게임에반영되는모습을볼수있고, 이렇게하는게군더기없는프로그램을제작할수있고, 계속사용하다보니편하게느껴지네요, fswindow.h fswindow.cpp가하는기능을소스코드를보시면주석이달려있으니참고하시기바람니다. 저역시완벽하게이해는못하고사용하는방법에대한정리를해드릴수있을뿐입니다. 대충한글주석을달아놨지만크게실뢰를하지마시길바람니다. ^_^;; 그리고대화상자에대한메시지처리는콜백함수에서처리해주시면되고, 사용법은윈도우의대화상자의컨트롤을사용하는것을그대로사용하시면, 됩니다, 윈도우 API 책을참고해서필요한버튼, 택스트창을붙여서사용하시바람니다. // 콜백함수에서대화상자입력처리 LRESULT CALLBACK Dial_MainMenu_Proc(HWND hdlg, UINT umsg, WPARAM wparam, LPARAM lparam) 자! 아래에풀소스를보시고원하는대화상자를만들어작업능률을올리시기바람니다. ^_^;; * 필요한소스들 fswindow.h,fswindow.cpp,fswexam.hfswexam.cpp,resource.h,resource.rc 자료는 KGDA 프로그램자료실에올리겠습니다 ^^:
fswexam.h // 예제프로그램헤더 #include <windows.h> #include <ddraw.h> #include <mmsystem.h> #include <windowsx.h> #include <stdio.h> #include "fswindow.h" #include "resource.h" // 16비트컬러의색상을만든다. #define _RGB16BIT555(r,g,b) ((b>>3) + ((g>>3) << 5) + ((r>>3) << 10)) #define _RGB16BIT565(r,g,b) ((b>>3) + ((g>>2) << 5) + ((r>>3) << 11)) // 비트맵고유식별자 #define BITMAP_ID #define MAXCOLOR 256 0x4D42 LPDIRECTDRAW7 lpdd7; LPDIRECTDRAWSURFACE7 lpddsprimary; LPDIRECTDRAWSURFACE7 lpddsback; DDCOLORKEY color_key; // 투명색 DDPIXELFORMAT ddpixel; BOOL BMP_STYLE_565 = FALSE; LPDIRECTDRAWSURFACE7 lpddswork; // 작업을완료하는페이지 // pcx 그림을위한표면 LPDIRECTDRAWSURFACE7 lpddsbmp; int mouse_x,mouse_y; RECT g_rcwindow; // 현재이미지가출력되는화면영역 RECT g_rcviewport; RECT g_rcscreen = 0,0,800,600; // 윈도우위치조절을위한변수 LONG ix,iy,iwidth,iheight;
// 프레임수체크용변수 DWORD dwframetime; DWORD dwframes; DWORD dwframecount; DWORD dwtime; DWORD acc_count; char FR_buff[256]; char FR_moubuff[300]; HDC g_hdc; // 표면의컨택스헨들을위해사용 // **************************************************************************** LPDIRECTDRAWPALETTE LPPALETTEENTRY lpddpal; lppepalette; BOOL blsactive; HWND hwnd; HDC hdc; HINSTANCE g_hinstance; // *************************************************************************************** HWND g_hwndhelp = NULL; HWND g_hwnd_workdlg = NULL; // 작업메뉴다이얼박스 // *************************************************************************************** bool x_flag = TRUE; bool y_flag = TRUE; // ************************************************************************************** long FAR PASCAL WindowProc( HWND hwnd, UINT message,wparam wparam, LPARAM lparam ); // ************************************************************************************** BOOL DDInitGame(HINSTANCE hinstance, int ncmdshow); int DDInitWindowed(void); int DDInitFullScreen(void); int GetSurfaceType(void); void DDEndGame(); void DDUpdateFrame();
HRESULT UpdateBounds(void); // ************************************************************************************** // 화면프레임수를출력한다. HRESULT DisplayFrameRate(void); // ************************************************************************************** // 1번대화상자입력처리 LRESULT CALLBACK Dial_MainMenu_Proc(HWND hdlg, UINT umsg, WPARAM wparam, LPARAM lparam); // 2번대화상자입력처리 LRESULT CALLBACK Dial_SubMenu_Proc(HWND hdlg, UINT umsg, WPARAM wparam, LPARAM lparam); // ************************************************************************************** // 폰트출력용함수 void PutString(HDC hdc,int x,int y,colorref cb,colorref ct,char *string); // ************************************************************************************** // 다이렉트 16비트표면생성함수 LPDIRECTDRAWSURFACE7 CreateSurface16(long s_width, long s_height, int red,int green,int blue); // 다이렉트표면을만들어서그표면에 (800*600) BMP IMAGE만로드 LPDIRECTDRAWSURFACE7 LoadBMP_800_600_16(LPSTR szfilename); // 현재만들어져있는다이렉트표면에비트맵이미지를읽어온다. bool GetBMP_800_600_16(LPDIRECTDRAWSURFACE7 w_surface,lpstr szfilename); // ************************************************************************************** fswexam.cpp // 예제프로그램소스 #include "fswexam.h" // ************************************************************************************** // 윈도우메인함수 - 프로그램이제일처음시작되는곳 // ************************************************************************************** int PASCAL WinMain( HINSTANCE hinstance, HINSTANCE hprevinstance, LPSTR lpcmdline, int ncmdshow ) MSG msg; WNDCLASS wc;
// 윈도우스타일결정 DWORD ExWinStyle, WinStyle; HANDLE hproc; wc.lpszclassname= "FSWINDOW EXAM"; wc.hinstance = hinstance; wc.lpfnwndproc = WindowProc; wc.style = CS_HREDRAW CS_VREDRAW; wc.cbclsextra = 0; wc.cbwndextra = 0; wc.hicon = LoadIcon (NULL,IDI_APPLICATION); wc.hcursor = LoadCursor (NULL,IDC_ARROW); wc.hbrbackground = (HBRUSH) GetStockObject(BLACK_BRUSH); wc.lpszmenuname = NULL; RegisterClass (&wc); #ifdef _DEBUG ExWinStyle = NULL; WinStyle = WS_OVERLAPPEDWINDOW & ~WS_CAPTION & ~WS_MAXIMIZEBOX & ~WS_SYSMENU; // [ 창크기계산 ] DWORD dwframewidth = GetSystemMetrics( SM_CXSIZEFRAME ); // 창프레임넙이 DWORD dwframeheight = GetSystemMetrics( SM_CYSIZEFRAME ); // 창프레이높이 DWORD dwmenuheight = 0; // GetSystemMetrics( SM_CYMENU ); // 메뉴높이 DWORD dwcaptionheight = GetSystemMetrics( SM_CYCAPTION ); // 창타이틀창높이 DWORD dwwindowwidth = 800 + dwframewidth * 2; // 양쪽프레임이니까 * 2 DWORD dwwindowheight = 600 + dwframeheight * 2 + dwcaptionheight; #else ExWinStyle = WS_EX_TOPMOST; WinStyle = WS_POPUP; // [ 창크기계산 ] DWORD dwframewidth = 0; // 창프레임넙이 DWORD dwframeheight = 0; // 창프레이높이 DWORD dwmenuheight = 0; // 메뉴높이 DWORD dwcaptionheight = 0; // 창타이틀창높이 DWORD dwwindowwidth = 800; // 양쪽프레임이니까 * 2 DWORD dwwindowheight = 600;
#endif hwnd = CreateWindowEx( ExWinStyle, // 창의확장된스타일지정 "FSWINDOW EXAM", // 클래스이름 "FSWINDOW EXAM", // 창이름 WinStyle, // 창의형태결정 CW_USEDEFAULT, // 창의수평위치 CW_USEDEFAULT, // 창의수직위치 dwwindowwidth, // 창의폭 dwwindowheight, // 창이높이 NULL, // 이창은父창이없음 NULL, // 메뉴는없음 hinstance, // 이인스턴스가이창을소유 NULL); // 포인터는필요없음 if(!hwnd) return FALSE; #ifdef _DEBUG // ******************************************************************************************** // 윈도우를화면정중앙에띄우자 GetWindowRect( hwnd, &g_rcwindow ); iwidth = g_rcwindow.right - g_rcwindow.left; iheight = g_rcwindow.bottom - g_rcwindow.top; ix = LONG((GetSystemMetrics(SM_CXSCREEN) - iwidth)/2); iy = LONG((GetSystemMetrics(SM_CYSCREEN) - iheight)/2); MoveWindow(hWnd,iX,iY,iWidth,iHeight,TRUE); // ******************************************************************************************** #endif ShowCursor(TRUE); ShowWindow (hwnd,ncmdshow); UpdateWindow (hwnd); // 현재프로그램의우선순위를높인다. -_-;; // 해보니까 5프레임정도빨라짐 hproc = GetCurrentProcess(); #ifdef _DEBUG SetPriorityClass (hproc, NORMAL_PRIORITY_CLASS); #else SetPriorityClass (hproc, HIGH_PRIORITY_CLASS); #endif
//********************************************************************************************* // 다이렉트드로우오브젝트생성 if(!ddinitgame(hinstance,ncmdshow)) PostQuitMessage(0); //******************************************************************************************** //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // 다이렉트드로우화면에대화상자사용을위한초기화작업 FSWindow_Init(hWnd, lpdd7, lpddsprimary, lpddsback); //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> //********************************************************************************************* // 다이얼박스를생성한다. g_hwnd_workdlg = CreateDialog(g_hInstance, MAKEINTRESOURCE(IDD_MAINMENU), hwnd, (DLGPROC) Dial_MainMenu_Proc); // 창이위치를변경한다. SetWindowPos(g_hWnd_WorkDlg,HWND_TOP,g_rcWindow.left+550,g_rcWindow.top+10,0,0,SWP_NOSIZE); ShowWindow(g_hWnd_WorkDlg, SW_SHOWNORMAL); //********************************************************************************************* //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // fswindow 시작 g_hwndhelp = FSWindow_Begin(g_hWnd_WorkDlg, FALSE); //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> //********************************************************************************************* #ifdef NDEBUG // 디버깅모드가아닌경우에실행할것들은여기에... #endif while( 1 ) if( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ) if(!getmessage( &msg, NULL, 0, 0 ) ) return msg.wparam; //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // 대화상자가보여지면, 모달리스대화상자를위해서메시지를교환시킨다.
if (g_hwnd_workdlg == (HWND )NULL!IsDialogMessage(g_hWnd_WorkDlg, &msg)) // Translate and dispatch the message TranslateMessage(&msg); DispatchMessage(&msg); // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> else if(blsactive) DDUpdateFrame(); else WaitMessage(); return 1; //********************************************************************************************* // 목적 : 게임초기화 //********************************************************************************************* BOOL DDInitGame(HINSTANCE hinstance, int ncmdshow) int e_v=0; // 에러값을가지고종료루틴을다르게처리한다. // 로드시오류가없는경우 0으로처리 #ifdef _DEBUG e_v = DDInitWindowed(); if(e_v!= 0) goto InitERROR; #else e_v = DDInitFullScreen(); if(e_v!= 0) goto InitERROR; #endif
//********************** // first load bmp image //********************** //3차표면만들기 lpddswork = CreateSurface16(800,600,0,0,0); // 배경이미지저장표면 lpddsbmp = LoadBMP_800_600_16("banner.bmp"); if(lpddsbmp == NULL) e_v=8; MessageBox(hWnd," 비트맵읽기오류 "," 게임실행오류 ",MB_OK); goto InitERROR; lpddswork->bltfast(0,0,lpddsbmp,&g_rcscreen,false); return TRUE; InitERROR : DDEndGame(); PostQuitMessage(0); return FALSE; //********************************************************************************************** // 목적 : 창모드로윈도우초기화 //********************************************************************************************** int DDInitWindowed(void) DDSURFACEDESC2 ddsd; HRESULT ddrval; LPDIRECTDRAWCLIPPER pcclipper; ddrval = DirectDrawCreateEx(NULL,(VOID**)&lpDD7,IID_IDirectDraw7,NULL); if(ddrval!= DD_OK) return 1;
ddrval = lpdd7->setcooperativelevel(hwnd,ddscl_normal); if(ddrval!= DD_OK) return 2; // Create the primary surface ZeroMemory( &ddsd, sizeof( ddsd ) ); ddsd.dwsize = sizeof( ddsd ); ddsd.dwflags = DDSD_CAPS; ddsd.ddscaps.dwcaps = DDSCAPS_PRIMARYSURFACE; ddrval = lpdd7->createsurface( &ddsd, &lpddsprimary, NULL ); if(ddrval!= DD_OK) MessageBox(hWnd," 다이렉트드로우 1차버퍼생성실패 "," 게임실행오류 ",MB_OK); return 4; // 에러리턴 // Create the backbuffer surface ddsd.dwflags = DDSD_CAPS DDSD_WIDTH DDSD_HEIGHT; ddsd.ddscaps.dwcaps = DDSCAPS_OFFSCREENPLAIN DDSCAPS_3DDEVICE; ddsd.dwwidth = 800; ddsd.dwheight = 600; ddrval = lpdd7->createsurface( &ddsd, &lpddsback, NULL ); if(ddrval!= DD_OK) MessageBox(hWnd," 다이렉트드로우 2차버퍼생성실패 "," 게임실행오류 ",MB_OK); return 5; ddrval = lpdd7->createclipper( 0, &pcclipper, NULL ); if(ddrval!= DD_OK) MessageBox(hWnd," 다이렉트드로우클리퍼생성실패 "," 게임실행오류 ",MB_OK); return 6;
ddrval = pcclipper->sethwnd( 0, hwnd ); if(ddrval!= DD_OK) pcclipper->release(); MessageBox(hWnd," 다이렉트드로우클리퍼설정실패 "," 게임실행오류 ",MB_OK); return 7; ddrval = lpddsprimary->setclipper( pcclipper ); if(ddrval!= DD_OK) pcclipper->release(); MessageBox(hWnd,"1차버퍼에클리퍼지정실패 "," 게임실행오류 ",MB_OK); return 8; // Done with clipper pcclipper->release(); UpdateBounds(); // 픽셀포맷설정 switch (GetSurfaceType()) // 전역변수값을변경한다. case 3: BMP_STYLE_565 = TRUE; case 4: BMP_STYLE_565 = FALSE; default: // 현재게임화면은 16비트컬러의경우에만처리한다, // 다른화면색상모드에대해서는에러처리한다. MessageBox(hWnd," 윈도우화면색상을하이컬러 [16비트색상 ] 으로맞춘후에 n다시실행해주시기바람니다!"," 게임실행오류 ",MB_OK); return 9; ;
return 0; // int DDInitWindowed(void) //********************************************************************************************** // 목적 : 화면컬러모드설정 //********************************************************************************************** int GetSurfaceType(void) DDSURFACEDESC2 dddesc = sizeof(dddesc) ; dddesc.ddscaps.dwcaps = DDSD_PIXELFORMAT; HRESULT ddrval; // 화면의 Device를잃었을때의처리부분 ddrval = lpddsback->getsurfacedesc(&dddesc); if(ddrval!= DD_OK) return 0; int nbpp = dddesc.ddpfpixelformat.dwrgbbitcount; DWORD DWORD DWORD RMask = dddesc.ddpfpixelformat.dwrbitmask; GMask = dddesc.ddpfpixelformat.dwgbitmask; BMask = dddesc.ddpfpixelformat.dwbbitmask; int m_colormode = -1;// 화면모드 switch (nbpp) case 8: m_colormode = 2; case 16: if (RMask == 0xf800 && GMask == 0x07e0 && BMask == 0x001f) m_colormode = 3;
// RGB565 else if(rmask == 0x7c00 && GMask == 0x03e0 && BMask == 0x001f) m_colormode = 4; // RGB555 else m_colormode = -1; // error case 24: if(rmask == 0xff0000 && GMask == 0xff00 && BMask == 0xff) m_colormode = 5; // RGB888 else if(rmask == 0xff && GMask == 0xff00 && BMask == 0xff0000) m_colormode = 6; // BGR888 else m_colormode = -1; // error case 32: if(rmask == 0xff0000 && GMask == 0xff00 && BMask == 0xff) m_colormode = 7; // XRGB888 else if(rmask == 0xff && GMask == 0xff00 && BMask == 0xff0000) m_colormode = 8; // XBGR888 else m_colormode = -1; // error default: m_colormode = -1; // error return m_colormode; //********************************************************************************************** // 목적 : 풀화면모드로초기화 //********************************************************************************************** int DDInitFullScreen(void) DDSURFACEDESC2 ddsd; DDSCAPS2 ddscaps; HRESULT ddrval; ddrval = DirectDrawCreateEx(NULL,(VOID**)&lpDD7,IID_IDirectDraw7,NULL); if(ddrval!= DD_OK) return 1;
ddrval = lpdd7->setcooperativelevel(hwnd,ddscl_exclusive DDSCL_FULLSCREEN); if(ddrval!= DD_OK) return 2; ddrval = lpdd7->setdisplaymode(800,600,16,0,0); if(ddrval!= DD_OK) return 3; // ready for using backbuffer ZeroMemory(&ddsd, sizeof(ddsd)); ddsd.dwsize = sizeof(ddsd); ddsd.dwflags = DDSD_CAPS DDSD_BACKBUFFERCOUNT; ddsd.ddscaps.dwcaps = DDSCAPS_PRIMARYSURFACE DDSCAPS_FLIP DDSCAPS_COMPLEX DDSCAPS_3DDEVICE; ddsd.dwbackbuffercount = 1; ddrval = lpdd7->createsurface(&ddsd, &lpddsprimary, NULL); if(ddrval!= DD_OK) return 4; ZeroMemory(&ddscaps, sizeof(ddscaps)); ddscaps.dwcaps = DDSCAPS_BACKBUFFER; ddrval = lpddsprimary->getattachedsurface(&ddscaps,&lpddsback); if(ddrval!= DD_OK) return 5; // 픽셀포맷설정
switch (GetSurfaceType()) // 전역변수값을변경한다. case 3: BMP_STYLE_565 = TRUE; case 4: BMP_STYLE_565 = FALSE; default: ; return 0; UpdateBounds(); //********************************************************************************************** // 목적 : 창모드에서화면이동등의갱신이발생한경우처리 //********************************************************************************************** HRESULT UpdateBounds(void) #ifdef _DEBUG GetClientRect( hwnd, &g_rcwindow ); ClientToScreen( hwnd, (POINT*)&g_rcWindow ); ClientToScreen( hwnd, (POINT*)&g_rcWindow+1 ); // 이방식을포인트함수사용하지않고이용했다. //GetClientRect(hWnd, &rcdest); //ClientToScreen(hWnd, &pt); //OffsetRect(&rcDest, pt.x, pt.y); #else
SetRect(&g_rcWindow,0,0,GetSystemMetrics(SM_CXSCREEN),GetSystemMetrics(SM_CYSCREEN) ); #endif return S_OK; //********************************************************************************************** // 목적 : 게임종료시크리어 //********************************************************************************************** void DDEndGame(void) if(lpdd7!= NULL) if(lpddsprimary!= NULL) lpddsprimary->release(); lpddsprimary = NULL; lpdd7->release(); lpdd7 = NULL; //********************************************************************************************** // 목적 : 화면관련업데이트를해주는부분 //********************************************************************************************** void DDUpdateFrame() HRESULT ddrval; // 작업표면에출력 lpddswork->bltfast(0,0,lpddsbmp,&g_rcscreen,false); // 대부분의그래픽작업은여기서다수행한다. // ********************************************************************************** // 2차표면에작업표면을출력한다.
lpddsback->bltfast(0,0,lpddswork,&g_rcscreen,false); // ********************************************************************************** DisplayFrameRate(); // 현재프레임수를출력한다. // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // 윈도우창과함께사용할경우에는다음과같이출력부를맞추어주어야한다. if (FSWindow_IsActive()) FSWindow_Update(); else // 에디터모드에서는 Blt 방식만을이용한다. ddrval = lpddsprimary->blt(&g_rcwindow, lpddsback, NULL, DDBLT_WAIT, NULL); /* #ifdef _DEBUG ddrval = lpddsprimary->blt(&g_rcwindow, lpddsback, NULL, DDBLT_WAIT, NULL); #else ddrval = lpddsprimary->bltfast(0,0,lpddsback,&g_rcwindow,ddbltfast_nocolorkey); if(ddrval == DD_OK) return; #endif */ // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> if(ddrval == DDERR_SURFACELOST) ddrval = lpddsprimary->restore(); if(ddrval!= DD_OK) MessageBox(hWnd,"lpDDSPrimary->Restore() failed!!!",null,mb_ok); return; //end FSWindow
//********************************************************************************************** // 목적 : 윈도우프로시져 - 메시지를받아처리한다. //********************************************************************************************** long FAR PASCAL WindowProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam ) switch( message ) case WM_MOVE: // Retrieve the window position after a move. // [ 이동후에창의위치구하기 ] if( lpdd7 ) UpdateBounds(); return 0L; case WM_SIZE: if( lpdd7 ) UpdateBounds(); case WM_GETMINMAXINFO: // Don't allow resizing in windowed mode. // [ 창의크기를조절하는것을막는다.] // Fix the size of the window to 640x480 (client size) // [ 창의크기는지정한크기로유지 ] MINMAXINFO* pminmax = (MINMAXINFO*) lparam; DWORD dwframewidth = GetSystemMetrics( SM_CXSIZEFRAME ); DWORD dwframeheight = GetSystemMetrics( SM_CYSIZEFRAME ); DWORD dwmenuheight = GetSystemMetrics( SM_CYMENU ); DWORD dwcaptionheight = GetSystemMetrics( SM_CYCAPTION ); pminmax->ptmintracksize.x = 800 + dwframewidth * 2;
pminmax->ptmintracksize.y = 600 + dwframeheight*2 + 0 + dwcaptionheight; pminmax->ptmaxtracksize.x = pminmax->ptmintracksize.x; pminmax->ptmaxtracksize.y = pminmax->ptmintracksize.y; return 0L; case WM_MOUSEMOVE: mouse_x = LOWORD(lParam); mouse_y = HIWORD(lParam); case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: case WM_KEYDOWN: switch( wparam ) case VK_SPACE: if(g_hwnd_workdlg == NULL) // 작업메뉴를생성한다. // 대화상자새로생성 g_hwnd_workdlg = CreateDialog(g_hInstance, MAKEINTRESOURCE(IDD_MAINMENU), hwnd, (DLGPROC) Dial_MainMenu_Proc); // 창이위치를변경한다. SetWindowPos(g_hWnd_WorkDlg,HWND_TOP, g_rcwindow.left+550,g_rcwindow.top+10,0,0,swp_nosize); ShowWindow(g_hWnd_WorkDlg, SW_SHOWNORMAL); // fswindow 시작 g_hwndhelp = FSWindow_Begin(g_hWnd_WorkDlg, FALSE); case VK_ESCAPE: case VK_F12: // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // 대화상자닫기 EndDialog(g_hWnd_WorkDlg, TRUE); FSWindow_End();
g_hwnd_workdlg = (HWND)NULL; // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // 메인윈도우종료 PostMessage( hwnd, WM_CLOSE, 0, 0 ); case WM_ACTIVATEAPP: blsactive = (BOOL)wParam; if(blsactive) if(lpddsprimary!= NULL && lpddsprimary->islost() == DDERR_SURFACELOST) lpddsprimary->restore(); case WM_SYSCOMMAND: // 풀화면모드에서이동, 사이징, 파워를잃는것을막는다. switch( wparam ) case SC_MOVE: case SC_SIZE: case SC_MAXIMIZE: case SC_MONITORPOWER: blsactive = TRUE; return TRUE; case WM_SETCURSOR: SetCursor(NULL); case WM_DESTROY: blsactive = FALSE; DDEndGame(); PostQuitMessage( 0 );
return DefWindowProc( hwnd, message, wparam, lparam ); // ************************************************************************************** // 목적 : 화면에현재의초당스레임수를나타낸다. // ************************************************************************************** HRESULT DisplayFrameRate(void) dwframecount++; dwtime=timegettime() - dwframetime; if(dwtime > 1000) //1초안에대한처리 dwframes = (dwframecount * 1000)/dwTime; dwframetime = timegettime(); dwframecount = 0; acc_count++; wsprintf(fr_buff," 현재게임프레임수 : %d 누적카운트 %d 0",dwFrames,acc_count); if(lpddsback->getdc(&g_hdc) == DD_OK) PutString(g_hdc,11,1,RGB(0,0,0),RGB(0,0,0),FR_buff); PutString(g_hdc,10,0,RGB(0,0,0),RGB(255,255,255),FR_buff); lpddsback->releasedc(g_hdc); return S_OK; /* DisplayFrameRate */ // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> //*************************************************************************************** // 목적 : 대화상자 1 로오는메시지를처리한다. //*************************************************************************************** LRESULT CALLBACK Dial_MainMenu_Proc(HWND hdlg, UINT umsg, WPARAM wparam, LPARAM lparam)
switch (umsg) case WM_INITDIALOG: // 처음대화상자가생성될때초기화할것은여기에... return TRUE; case WM_COMMAND: switch(loword(wparam)) case IDC_BUTTON_MOVE2: //2번대화상자로이동 // 대화상자제거 EndDialog(hDlg, TRUE); FSWindow_End(); g_hwnd_workdlg = (HWND)NULL; // 대화상자 2 생성 g_hwnd_workdlg = CreateDialog(g_hInstance, MAKEINTRESOURCE(IDD_SUBMENU), hwnd, (DLGPROC) Dial_SubMenu_Proc); // 창이위치를변경한다. SetWindowPos(g_hWnd_WorkDlg,HWND_TOP,g_rcWindow.left+550, g_rcwindow.top+10,0,0,swp_nosize); ShowWindow(g_hWnd_WorkDlg, SW_SHOWNORMAL); // fswindow 시작 g_hwndhelp = FSWindow_Begin(g_hWnd_WorkDlg, FALSE); case IDC_BUTTON_OUT: // 대화상자감추기 EndDialog(hDlg, TRUE); FSWindow_End(); g_hwnd_workdlg = (HWND)NULL; case IDC_EXIT:
if(messagebox(g_hwnd_workdlg, " 프로그램을종료할까요?"," 확인 ",MB_YESNO) == IDYES) EndDialog(g_hWnd_WorkDlg, TRUE); FSWindow_End(); g_hwnd_workdlg = (HWND)NULL; PostMessage(hWnd, WM_CLOSE, 0, 0 ); case WM_CLOSE: if(messagebox(g_hwnd_workdlg," 프로그램을종료할까요?", " 확인 ",MB_YESNO) == IDYES) EndDialog(g_hWnd_WorkDlg, TRUE); FSWindow_End(); g_hwnd_workdlg = (HWND)NULL; PostMessage(hWnd, WM_CLOSE, 0, 0 ); return FALSE; // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> //*************************************************************************************** // 목적 : 대화상자 2 로오는메시지를처리한다. //*************************************************************************************** LRESULT CALLBACK Dial_SubMenu_Proc(HWND hdlg, UINT umsg, WPARAM wparam, LPARAM lparam) switch (umsg) case WM_INITDIALOG: // 처음대화상자가생성될때초기화할것은여기에... return TRUE;
case WM_COMMAND: switch(loword(wparam)) case IDC_BUTTON_MOVE1: //2번대화상자를제거 EndDialog(hDlg, TRUE); FSWindow_End(); g_hwnd_workdlg = (HWND)NULL; //1번대화상자를만든다. // 대화상자새로생성 g_hwnd_workdlg = CreateDialog(g_hInstance, MAKEINTRESOURCE(IDD_MAINMENU), hwnd, (DLGPROC) Dial_MainMenu_Proc); // 창이위치를변경한다. SetWindowPos(g_hWnd_WorkDlg,HWND_TOP,g_rcWindow.left+550, g_rcwindow.top+10,0,0,swp_nosize); ShowWindow(g_hWnd_WorkDlg, SW_SHOWNORMAL); // fswindow 시작 g_hwndhelp = FSWindow_Begin(g_hWnd_WorkDlg, FALSE); return FALSE; // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // 문자열출력용함수 void PutString(HDC hdc,int x,int y,colorref cb,colorref ct,char *string) SetBkMode(hdc,TRANSPARENT); SetBkColor(hdc,cb); SetTextColor(hdc,ct); TextOut(hdc,x,y,string,strlen(string));
// ************************************************************************************** // 16비트다이렉트표면을만든다. LPDIRECTDRAWSURFACE7 CreateSurface16(long s_width, long s_height, int red,int green,int blue) DDSURFACEDESC2 ddsd; LPDIRECTDRAWSURFACE7 lpdds; unsigned short *lpvideo; unsigned short pixel = 0; int DS_lPitch = 0; // create ddsurface ZeroMemory(&ddsd,sizeof(ddsd)); ddsd.dwsize = sizeof(ddsd); ddsd.dwflags = DDSD_CAPS DDSD_HEIGHT DDSD_WIDTH; ddsd.ddscaps.dwcaps = DDSCAPS_OFFSCREENPLAIN DDSCAPS_SYSTEMMEMORY; ddsd.dwwidth = s_width; ddsd.dwheight = s_height; if(lpdd7->createsurface(&ddsd, &lpdds, NULL)!= DD_OK) return NULL; ddsd.dwsize = sizeof(ddsd); lpdds->lock(null,&ddsd,ddlock_wait DDLOCK_SURFACEMEMORYPTR,NULL); lpvideo = (unsigned short *)ddsd.lpsurface; lpdds->unlock(null); // 비디오카드마다다른포맷을가진다. if(bmp_style_565) pixel = _RGB16BIT565(red,green,blue); else pixel = _RGB16BIT555(red,green,blue); for(int y = 0; y<s_height; y++) DS_lPitch = (ddsd.lpitch/2)*y;
for(int x = 0; x<s_width; x++) lpvideo[ds_lpitch+x] = pixel; return lpdds; // **************************************************************************** // 비트맵화일을로드해서표면에기록한다. // **************************************************************************** LPDIRECTDRAWSURFACE7 LoadBMP_800_600_16(LPSTR szfilename) FILE *fp; int red,green,blue; // 화일헨들 int line_x; int line_y; BITMAPFILEHEADER bitmapfileheader; // this contains the bitmapfile header BITMAPINFOHEADER bitmapinfoheader; // this is all the info including the palette DDSURFACEDESC2 ddsd; LPDIRECTDRAWSURFACE7 lpdsbmp; unsigned short *lpvideo; unsigned short *Cur_lpVideo; // 화일을연다 if(null == (fp=fopen(szfilename,"rb"))) return NULL; //bitmap file header 읽기 if(fread(&bitmapfileheader,sizeof(bitmapfileheader),1,fp) == -1) return NULL; //bitmap file 인지확인 if (bitmapfileheader.bftype!=bitmap_id) // 화일닫기 fclose(fp); // 비트맵화일이아님 NULL을리턴 return(null); // end if
// the bitmap infoheader 를읽는다. if(fread(&bitmapinfoheader,sizeof(bitmapinfoheader),1,fp) == -1) return NULL; // 256컬러모드일경우팔레트를로드한다. if (bitmapinfoheader.bibitcount == 8) // 화일닫기 fclose(fp); return NULL; // end if ZeroMemory(&ddsd,sizeof(ddsd)); ddsd.dwsize = sizeof(ddsd); ddsd.dwflags = DDSD_CAPS DDSD_HEIGHT DDSD_WIDTH; ddsd.ddscaps.dwcaps = DDSCAPS_OFFSCREENPLAIN DDSCAPS_SYSTEMMEMORY; ddsd.dwwidth = 800; ddsd.dwheight = 600; if(lpdd7->createsurface(&ddsd, &lpdsbmp, NULL)!= DD_OK) return NULL; ddsd.dwsize = sizeof(ddsd); lpdsbmp->lock(null,&ddsd,ddlock_wait DDLOCK_SURFACEMEMORYPTR,NULL); lpvideo = (unsigned short *)ddsd.lpsurface; if (bitmapinfoheader.bibitcount==8 bitmapinfoheader.bibitcount==16 bitmapinfoheader.bibitcount==24) // 마지막라인주소를계산한다. Cur_lpVideo = &lpvideo[599*800]; // 라인단위로역으로기록한다. for(line_y=0; line_y < 600 ; line_y++) if(bmp_style_565) for(line_x = 0; line_x < 800; line_x++) red = fgetc(fp);
green = fgetc(fp); blue = fgetc(fp); *Cur_lpVideo = _RGB16BIT565(blue,green,red); Cur_lpVideo++; else for(line_x = 0; line_x < 800; line_x++) red = fgetc(fp); green = fgetc(fp); blue = fgetc(fp); *Cur_lpVideo = _RGB16BIT555(blue,green,red); Cur_lpVideo++; // 다음시작주소를계산한다. Cur_lpVideo = &lpvideo[(598-line_y)*800]; //end for // end if else fclose(fp); return NULL; // end else lpdsbmp->unlock(null); // close the file fclose(fp); // return success return(lpdsbmp);
// **************************************************************************** // // 24비트 800*600의비트맵화일을지정된 16비트컬러로다이렉트표면에기록한다. // // **************************************************************************** // 작업페이지 bool GetBMP_800_600_16(LPDIRECTDRAWSURFACE7 w_surface,lpstr szfilename) FILE *fp; int red,green,blue; // 화일헨들 int line_x; int line_y; BITMAPFILEHEADER bitmapfileheader; // this contains the bitmapfile header BITMAPINFOHEADER bitmapinfoheader; // this is all the info including the palette DDSURFACEDESC2 ddsd; unsigned short *lpvideo; unsigned short *Cur_lpVideo; // 화일을연다 if(null == (fp=fopen(szfilename,"rb"))) return false; //bitmap file header 읽기 if(fread(&bitmapfileheader,sizeof(bitmapfileheader),1,fp) == -1) return false; //bitmap file 인지확인 if (bitmapfileheader.bftype!=bitmap_id) // 화일닫기 fclose(fp); // 비트맵화일이아님 NULL을리턴 return(false); // end if // the bitmap infoheader 를읽는다. if(fread(&bitmapinfoheader,sizeof(bitmapinfoheader),1,fp) == -1)
fclose(fp); return false; // 256컬러모드일경우처리하지않는다. if (bitmapinfoheader.bibitcount == 8) fclose(fp); return false; ddsd.dwsize = sizeof(ddsd); //bmp를읽어올표면에락을건다. w_surface->lock(null,&ddsd,ddlock_wait DDLOCK_SURFACEMEMORYPTR,NULL); lpvideo = (unsigned short *)ddsd.lpsurface; // 시작주소 if (bitmapinfoheader.bibitcount==8 bitmapinfoheader.bibitcount==16 bitmapinfoheader.bibitcount==24) // 마지막라인주소를계산한다. Cur_lpVideo = &lpvideo[599*800]; // 라인단위로역으로기록한다. for(line_y=0; line_y < 600 ; line_y++) if(bmp_style_565) for(line_x = 0; line_x < 800; line_x++) red = fgetc(fp); green = fgetc(fp); blue = fgetc(fp); *Cur_lpVideo = _RGB16BIT565(blue,green,red); Cur_lpVideo++;
else for(line_x = 0; line_x < 800; line_x++) red = fgetc(fp); green = fgetc(fp); blue = fgetc(fp); *Cur_lpVideo = _RGB16BIT555(blue,green,red); Cur_lpVideo++; // 다음시작주소를계산한다. Cur_lpVideo = &lpvideo[(598-line_y)*800]; //end for // end if else fclose(fp); return false; // end else w_surface->unlock(null); // close the file fclose(fp); // return success return true; 샘플코드는 KGDA 프로그램자료실에올리겠습니다. 오타, 누락, 기타궁금한사항은 dongcm@chol.com으로해주시기바람니다.