WINAPI - sokoban
내장형 하드웨어/WINAPI / 2011. 10. 10. 16:29
- 소코반은 여기저기 흩어져 있는 짐을 밀어서 창고에 순서대로 쌓는 게임이다.
- 소스 작성(제작 중)
→ 기본화면 출력
- 구현해야 할 점
→ 케릭터가 이동시 배경 벽돌을 넘어가지 않게 한다.
→ 옮겨야 하는 벽돌의 위치로 캐릭터가 이동하면 벽돌은 캐릭터가 이동해온 방향과 같은 방향으로 한칸씩 이동한다.
→ 벽돌이 제일 오른쪽 창고에 지정한 위치로 도달하게 되면 게임이 클리어 되도록 한다.
- 소스 작성(제작 중)
#include<windows.h>
#include "resource.h"
#define BLOCK 30
void DrawBitmap(HDC hdc, int x, int y, HBITMAP hBit);
/* 고정 형식 */
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // 메세지 처리 함수
HINSTANCE g_hInst; // instance 전역 변수 선언
LPCTSTR lpszClass = TEXT("First"); // 윈도 클래스 이름 정의
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
{
HWND hWnd;
MSG Message; // MSG 구조체 사용
WNDCLASS WndClass; // WndClass 구조체 사용
g_hInst = hInstance; // 윈도우 클래스를 등록하는 프로그램의 번호이며 WinMain의 인수로 전달된 hInstance값을 그대로 대입
/* 윈도우 클래스 정의 */
WndClass.cbClsExtra = 0; // 예약 영역, 사용하지 않을 경우는 0
WndClass.cbWndExtra = 0;
WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // HBRUSH 타입으로 형변환
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); // 윈도우가 사용할 마우스 커서지정
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); // 윈도우가 사용할 아이콘 지정
WndClass.hInstance = hInstance;
WndClass.lpfnWndProc = WndProc; // 윈도우의 메시지 처리 함수를 지정
WndClass.lpszClassName = lpszClass; // 윈도우 클래스의 이름을 문자열로 정의 - 여기서는 First라는 문자열을 보내준다.
WndClass.lpszMenuName = NULL; // 프로그램이 사용할 메뉴를 지정
WndClass.style = CS_HREDRAW|CS_VREDRAW; // 윈도우가 어떤 형태를 가질 것인가-윈도우의크기가 변할 경우 윈도우를 다시 그린다.
/* 윈도우 클래스 등록 */
RegisterClass(&WndClass); // WndClass 구조체의 번지를 전달
// 메모리 상에 윈도우 생성
hWnd=CreateWindow(lpszClass, TEXT("Sokoban!!!"), WS_CAPTION|WS_SYSMENU|WS_MAXIMIZEBOX|WS_MINIMIZEBOX, 30, 30, (BLOCK*25)+6, (BLOCK*15)+33, NULL, (HMENU)NULL, hInstance, NULL);
/* 인자의 의미 -> (윈도우 클래스 이름, 타이틀바에 나타날 문자열-윈도우 네임(클래스 이름과 같게 하려면 lpszClass),
윈도우의 형태 지정, 윈도우의 크기와 위치(픽셀단위)-x, y, nWidth, nHeight,
부모 윈도우가 있을 경우 부모윈도우의 핸들-hWndParent, 윈도우에서 사용할 메뉴의 핸들-hmenu,
프로그램의 핸들 지정-hinst(프로그램이 종료될 때 파괴되지 않은 윈도우를 자동으로 파괴),
여러개의 윈도우를 만들때 각 윈도우에 고유의 파라미터를 전달하는 특수 목적에 사용-lpvParam)
*/
/* 화면에 윈도우 표시 */
ShowWindow(hWnd, nCmdShow); // CreateWindow 함수가 리턴한 핸들을 이용하여 출력
/* 사용자로부터의 메시지 처리*/
while(GetMessage(&Message, NULL, 0, 0)) // 메시지 큐에서 메시지 읽어옴,
// WN_QUIT이면 False 리턴 후 while문을 빠져나간다.
{
TranslateMessage(&Message); // 키보드 입력 처리
// 만약 A키를 누르면 A문자가 입력되었다는 메시지를 만들어 낸다.
DispatchMessage(&Message); // 큐에서 꺼낸 메시지를 WndProc 함수의 iMAssage로 전달
}
return (int)Message.wParam; // 메시지 루프 종료 후 이 프로그램을 실행시킨 OS로 리턴
}
// 비트맵을 간단하게 출력할 수 있는 레퍼 함수
void DrawBitmap(HDC hdc, int x, int y, HBITMAP hBit)
{
HDC MemDC;
HBITMAP OldBitmap;
int bx, by;
BITMAP bit;
MemDC = CreateCompatibleDC(hdc);
OldBitmap = (HBITMAP)SelectObject(MemDC, hBit);
GetObject(hBit, sizeof(BITMAP), &bit); // 비트맵의 크기 조사
bx = bit.bmWidth;
by = bit.bmHeight;
BitBlt(hdc, x, y, bx, by, MemDC, 0, 0, SRCCOPY);
SelectObject(MemDC, OldBitmap);
DeleteDC(MemDC);
}
// 사용자와 시스템이 보내오는 메시지를 처리
// 운영체제가 호출하는 함수는 CALLBACK 표시, LRESULT = 4byte long
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
// +1은 "를 포함하며 이것은 NULL을 집어넣는다.
// 화면에 1대 1 대응시키기 위한 것
static unsigned char ucStage[][15][25+1]=
{
{ "#########################"
, "#########################"
, "#########################"
, "####### ###############"
, "#######$ ###############"
, "####### $###############"
, "##### $ $ ##############"
, "##### # ## ##############"
, "### # ## ######## ##"
, "### $ $ ##"
, "####### ###### #@## ##"
, "####### ##########"
, "#########################"
, "#########################"
, "#########################"
}
}; // 가로, 세로
HDC hdc;
PAINTSTRUCT ps;
static HBITMAP MyBitmap1; // 배경 블록을 불러오기 위한 변수
static HBITMAP MyBitmap2; // 캐릭터 불러오기 위한 변수
static HBITMAP MyBitmap3; // 움직이는 블록을 불러오기 위한 변수
int iCount_x;
int iCount_y;
static int hero_x=BLOCK*16; // 캐릭터 좌표, 좌표값을 기억해야 하므로 static변수로 선언
static int hero_y=BLOCK*10;
int mBlock; // 움직이는 블록
switch(iMessage) // 운영체제로부터 들어온 메시지에 대한 처리
{
/* 고정 형식 */
case WM_CREATE:
MyBitmap1 = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP1)); // 비트맵을 읽는다.(배경 블록)
MyBitmap2 = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP3)); // 비트맵을 읽는다.(캐릭터)
MyBitmap3 = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP2)); // 움직이는 블록
case WM_KEYDOWN:
switch(wParam)
{
// 키 입력에 따른 히어로의 좌표 변화
case VK_LEFT:
hero_x = hero_x-BLOCK;
break;
case VK_RIGHT:
hero_x = hero_x+BLOCK;
break;
case VK_UP:
hero_y = hero_y-BLOCK;
break;
case VK_DOWN:
hero_y = hero_y+BLOCK;
break;
}
// 조정된 좌표가 화면에 반영되도록 InvalidateRect(hWnd, NULL, TRUE); 사용.
// hWnd->무효화 대상 윈도우(다시그려져야 되는),NULL->윈도우 전영역 다시 그린다,TRUE->배경을 지우고 다시 그린다.
InvalidateRect(hWnd, NULL, TRUE);
return 0;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
for(iCount_x = 0; iCount_x<(BLOCK*25); (iCount_x=iCount_x+BLOCK))
{
for(iCount_y =0; iCount_y<(BLOCK*15); (iCount_y=iCount_y+BLOCK))
{
// 배경 블록 출력
if('#' == ucStage[0][iCount_y/BLOCK][iCount_x/BLOCK])
{
DrawBitmap(hdc, iCount_x, iCount_y, MyBitmap1); // 비트맵 출력
}
// 캐릭터 출력
else if('@' == ucStage[0][iCount_y/BLOCK][iCount_x/BLOCK])
{
DrawBitmap(hdc, hero_x, hero_y, MyBitmap2);
}
// 움직이는 블록 출력
else if('$' == ucStage[0][iCount_y/BLOCK][iCount_x/BLOCK])
{
DrawBitmap(hdc, iCount_x, iCount_y, MyBitmap3);
}
}
}
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY: // 운영체제로부터 들어온 메시지에 대한 처리 - 여기서는 윈도우가 파괴되었음을 알린다.
DeleteObject(MyBitmap1); // 비트맵 해제
DeleteObject(MyBitmap2); // 비트맵 해제
DeleteObject(MyBitmap3); // 비트맵 해제
PostQuitMessage(0); // WM_QUIT 메시지를 보냄
return 0;
}
// switch 문에서 처리하지 않은 메시지를 처리
return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}
#include "resource.h"
#define BLOCK 30
void DrawBitmap(HDC hdc, int x, int y, HBITMAP hBit);
/* 고정 형식 */
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // 메세지 처리 함수
HINSTANCE g_hInst; // instance 전역 변수 선언
LPCTSTR lpszClass = TEXT("First"); // 윈도 클래스 이름 정의
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
{
HWND hWnd;
MSG Message; // MSG 구조체 사용
WNDCLASS WndClass; // WndClass 구조체 사용
g_hInst = hInstance; // 윈도우 클래스를 등록하는 프로그램의 번호이며 WinMain의 인수로 전달된 hInstance값을 그대로 대입
/* 윈도우 클래스 정의 */
WndClass.cbClsExtra = 0; // 예약 영역, 사용하지 않을 경우는 0
WndClass.cbWndExtra = 0;
WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // HBRUSH 타입으로 형변환
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); // 윈도우가 사용할 마우스 커서지정
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); // 윈도우가 사용할 아이콘 지정
WndClass.hInstance = hInstance;
WndClass.lpfnWndProc = WndProc; // 윈도우의 메시지 처리 함수를 지정
WndClass.lpszClassName = lpszClass; // 윈도우 클래스의 이름을 문자열로 정의 - 여기서는 First라는 문자열을 보내준다.
WndClass.lpszMenuName = NULL; // 프로그램이 사용할 메뉴를 지정
WndClass.style = CS_HREDRAW|CS_VREDRAW; // 윈도우가 어떤 형태를 가질 것인가-윈도우의크기가 변할 경우 윈도우를 다시 그린다.
/* 윈도우 클래스 등록 */
RegisterClass(&WndClass); // WndClass 구조체의 번지를 전달
// 메모리 상에 윈도우 생성
hWnd=CreateWindow(lpszClass, TEXT("Sokoban!!!"), WS_CAPTION|WS_SYSMENU|WS_MAXIMIZEBOX|WS_MINIMIZEBOX, 30, 30, (BLOCK*25)+6, (BLOCK*15)+33, NULL, (HMENU)NULL, hInstance, NULL);
/* 인자의 의미 -> (윈도우 클래스 이름, 타이틀바에 나타날 문자열-윈도우 네임(클래스 이름과 같게 하려면 lpszClass),
윈도우의 형태 지정, 윈도우의 크기와 위치(픽셀단위)-x, y, nWidth, nHeight,
부모 윈도우가 있을 경우 부모윈도우의 핸들-hWndParent, 윈도우에서 사용할 메뉴의 핸들-hmenu,
프로그램의 핸들 지정-hinst(프로그램이 종료될 때 파괴되지 않은 윈도우를 자동으로 파괴),
여러개의 윈도우를 만들때 각 윈도우에 고유의 파라미터를 전달하는 특수 목적에 사용-lpvParam)
*/
/* 화면에 윈도우 표시 */
ShowWindow(hWnd, nCmdShow); // CreateWindow 함수가 리턴한 핸들을 이용하여 출력
/* 사용자로부터의 메시지 처리*/
while(GetMessage(&Message, NULL, 0, 0)) // 메시지 큐에서 메시지 읽어옴,
// WN_QUIT이면 False 리턴 후 while문을 빠져나간다.
{
TranslateMessage(&Message); // 키보드 입력 처리
// 만약 A키를 누르면 A문자가 입력되었다는 메시지를 만들어 낸다.
DispatchMessage(&Message); // 큐에서 꺼낸 메시지를 WndProc 함수의 iMAssage로 전달
}
return (int)Message.wParam; // 메시지 루프 종료 후 이 프로그램을 실행시킨 OS로 리턴
}
// 비트맵을 간단하게 출력할 수 있는 레퍼 함수
void DrawBitmap(HDC hdc, int x, int y, HBITMAP hBit)
{
HDC MemDC;
HBITMAP OldBitmap;
int bx, by;
BITMAP bit;
MemDC = CreateCompatibleDC(hdc);
OldBitmap = (HBITMAP)SelectObject(MemDC, hBit);
GetObject(hBit, sizeof(BITMAP), &bit); // 비트맵의 크기 조사
bx = bit.bmWidth;
by = bit.bmHeight;
BitBlt(hdc, x, y, bx, by, MemDC, 0, 0, SRCCOPY);
SelectObject(MemDC, OldBitmap);
DeleteDC(MemDC);
}
// 사용자와 시스템이 보내오는 메시지를 처리
// 운영체제가 호출하는 함수는 CALLBACK 표시, LRESULT = 4byte long
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
// +1은 "를 포함하며 이것은 NULL을 집어넣는다.
// 화면에 1대 1 대응시키기 위한 것
static unsigned char ucStage[][15][25+1]=
{
{ "#########################"
, "#########################"
, "#########################"
, "####### ###############"
, "#######$ ###############"
, "####### $###############"
, "##### $ $ ##############"
, "##### # ## ##############"
, "### # ## ######## ##"
, "### $ $ ##"
, "####### ###### #@## ##"
, "####### ##########"
, "#########################"
, "#########################"
, "#########################"
}
}; // 가로, 세로
HDC hdc;
PAINTSTRUCT ps;
static HBITMAP MyBitmap1; // 배경 블록을 불러오기 위한 변수
static HBITMAP MyBitmap2; // 캐릭터 불러오기 위한 변수
static HBITMAP MyBitmap3; // 움직이는 블록을 불러오기 위한 변수
int iCount_x;
int iCount_y;
static int hero_x=BLOCK*16; // 캐릭터 좌표, 좌표값을 기억해야 하므로 static변수로 선언
static int hero_y=BLOCK*10;
int mBlock; // 움직이는 블록
switch(iMessage) // 운영체제로부터 들어온 메시지에 대한 처리
{
/* 고정 형식 */
case WM_CREATE:
MyBitmap1 = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP1)); // 비트맵을 읽는다.(배경 블록)
MyBitmap2 = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP3)); // 비트맵을 읽는다.(캐릭터)
MyBitmap3 = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP2)); // 움직이는 블록
case WM_KEYDOWN:
switch(wParam)
{
// 키 입력에 따른 히어로의 좌표 변화
case VK_LEFT:
hero_x = hero_x-BLOCK;
break;
case VK_RIGHT:
hero_x = hero_x+BLOCK;
break;
case VK_UP:
hero_y = hero_y-BLOCK;
break;
case VK_DOWN:
hero_y = hero_y+BLOCK;
break;
}
// 조정된 좌표가 화면에 반영되도록 InvalidateRect(hWnd, NULL, TRUE); 사용.
// hWnd->무효화 대상 윈도우(다시그려져야 되는),NULL->윈도우 전영역 다시 그린다,TRUE->배경을 지우고 다시 그린다.
InvalidateRect(hWnd, NULL, TRUE);
return 0;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
for(iCount_x = 0; iCount_x<(BLOCK*25); (iCount_x=iCount_x+BLOCK))
{
for(iCount_y =0; iCount_y<(BLOCK*15); (iCount_y=iCount_y+BLOCK))
{
// 배경 블록 출력
if('#' == ucStage[0][iCount_y/BLOCK][iCount_x/BLOCK])
{
DrawBitmap(hdc, iCount_x, iCount_y, MyBitmap1); // 비트맵 출력
}
// 캐릭터 출력
else if('@' == ucStage[0][iCount_y/BLOCK][iCount_x/BLOCK])
{
DrawBitmap(hdc, hero_x, hero_y, MyBitmap2);
}
// 움직이는 블록 출력
else if('$' == ucStage[0][iCount_y/BLOCK][iCount_x/BLOCK])
{
DrawBitmap(hdc, iCount_x, iCount_y, MyBitmap3);
}
}
}
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY: // 운영체제로부터 들어온 메시지에 대한 처리 - 여기서는 윈도우가 파괴되었음을 알린다.
DeleteObject(MyBitmap1); // 비트맵 해제
DeleteObject(MyBitmap2); // 비트맵 해제
DeleteObject(MyBitmap3); // 비트맵 해제
PostQuitMessage(0); // WM_QUIT 메시지를 보냄
return 0;
}
// switch 문에서 처리하지 않은 메시지를 처리
return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}
- 구현해야 할 점
→ 케릭터가 이동시 배경 벽돌을 넘어가지 않게 한다.
→ 옮겨야 하는 벽돌의 위치로 캐릭터가 이동하면 벽돌은 캐릭터가 이동해온 방향과 같은 방향으로 한칸씩 이동한다.
→ 벽돌이 제일 오른쪽 창고에 지정한 위치로 도달하게 되면 게임이 클리어 되도록 한다.
'내장형 하드웨어 > WINAPI' 카테고리의 다른 글
영상처리 - 영상에 이미지 파일 고정 출력 및 위치변환 (0) | 2011.11.24 |
---|---|
영상 처리 - 복제 화면 띄우기, RGB값 변화에 따른 화면 출력 (1) | 2011.11.23 |
WINAPI - 모델리스형 대화상자 (0) | 2011.10.06 |
WINAPI - DlgCheck (0) | 2011.09.29 |
WINAPI - 컨트롤과의 통신 (0) | 2011.09.28 |