2015-11-19
* WinAPI
#PUSH PUSH
전날 소스
main.c
금일 개발 부분
맵 그리기
UCHAR ucStageMap[STAGE][YFRAME][XFRAME+1] = { { "###############" , "#@ #" , "# #" , "# #" , "# B. . #" , "# .B B #" , "# . #" , "# B #" , "# #" , "###############" }, { "###############" , "#@ #######" , "###### #######" , "#### #######" , "#### ## ####" , "#### # #.##" , "#### # B # ##" , "#### # ##" , "### ## ##" , "###############" } }; UCHAR ucMap[YFRAME][XFRAME + 1]; |
위와 같이 맵의 자료구조를 설정해 놓으면 판수에따른 다른 맵을 그릴 수 있다.
void LoadMap(void) { int iXCount; int iYCount;
for (iYCount = 0; iYCount < YFRAME; iYCount++) { for (iXCount = 0; iXCount < XFRAME; iXCount++) { //히어로의 시작위치 잡아주기 if ('@' == ucStageMap[uiStage][iYCount][iXCount]) { iXPos = iXCount; iYPos = iYCount; ucMap[iYCount][iXCount] = ' '; continue; }
ucMap[iYCount][iXCount] = ucStageMap[uiStage][iYCount][iXCount]; } }
return;
} |
LoadMap함수는 미리 지정해놓은 맵에서 히어로의 시작위치를 찾아주고 해당 판 수에 따른 맵을 변수에 저장하기 위함이다.
히어로 움직이는 영역 제한하기
히어로는 길과 점위만 이동할 수 있고 벽은 이동할 수 없다. 그리고 상자는 지나가는 개념이 아니고 다음 방향으로 밀어 줘야한다. 또 이때 상자 다음칸에 벽, 또다른 상자가 있으면 히어로는 이동할 수 없다.
이를 고려해서 코딩을 한다.
case VK_LEFT: HbmHero = HbmLeft; if (' ' == ucMap[iYPos][iXPos - 1]) { --iXPos; } else if ('.' == ucMap[iYPos][iXPos - 1]) { --iXPos; } else if ('B' == ucMap[iYPos][iXPos - 1]) { if (' ' == ucMap[iYPos][iXPos - 2]) // [box] [box] { ucMap[iYPos][iXPos - 2] = ucMap[iYPos][iXPos - 1]; //박스 밀고
if ('.' != ucStageMap[uiStage][iYPos][iXPos - 1]) { ucMap[iYPos][iXPos - 1] = ' '; } else { ucMap[iYPos][iXPos - 1] = '.'; } --iXPos; } else if ('.' == ucMap[iYPos][iXPos - 2]) { ucMap[iYPos][iXPos - 2] = ucMap[iYPos][iXPos - 1]; //박스 밀고
if ('.' != ucStageMap[uiStage][iYPos][iXPos - 1]) { ucMap[iYPos][iXPos - 1] = ' '; } else { ucMap[iYPos][iXPos - 1] = '.'; } --iXPos; } }
break;
--iXPos; ucMap[iYPos][iXPos] = '@'; break;
case VK_RIGHT:
HbmHero = HbmRight; if (' ' == ucMap[iYPos][iXPos+1]) { ++iXPos; stArea.right = (iXPos + 1) * XTILE; } else if ('.' == ucMap[iYPos][iXPos + 1]) { ++iXPos; stArea.right = (iXPos + 1) * XTILE; } else if ('B' == ucMap[iYPos][iXPos + 1]) { if (' ' == ucMap[iYPos][iXPos + 2]) // [box] { ucMap[iYPos][iXPos + 2] = ucMap[iYPos][iXPos + 1]; //박스 밀고
if ('.' != ucStageMap[uiStage][iYPos][iXPos+1]) { ucMap[iYPos][iXPos + 1] = ' '; } else { ucMap[iYPos][iXPos + 1] = '.'; } ++iXPos; stArea.right = (iXPos + 2) * XTILE;
} else if ('.' == ucMap[iYPos][iXPos + 2]) { ucMap[iYPos][iXPos + 2] = ucMap[iYPos][iXPos + 1]; //박스 밀고
if ('.' != ucStageMap[uiStage][iYPos][iXPos + 1]) { ucMap[iYPos][iXPos + 1] = ' '; } else { ucMap[iYPos][iXPos + 1] = '.'; } ++iXPos; } } break;
case VK_UP: HbmHero = HbmFront;
if (' ' == ucMap[iYPos - 1][iXPos]) { --iYPos; } else if ('.' == ucMap[iYPos - 1][iXPos]) { --iYPos; } else if ('B' == ucMap[iYPos - 1][iXPos]) { if (' ' == ucMap[iYPos - 2][iXPos]) // [box] { ucMap[iYPos - 2][iXPos] = ucMap[iYPos - 1][iXPos]; //박스 밀고
if ('.' != ucStageMap[uiStage][iYPos - 1][iXPos]) { ucMap[iYPos - 1][iXPos] = ' '; } else { ucMap[iYPos - 1][iXPos] = '.'; }
--iYPos; } else if ('.' == ucMap[iYPos - 2][iXPos]) { ucMap[iYPos - 2][iXPos] = ucMap[iYPos - 1][iXPos]; //박스 밀고
if ('.' != ucStageMap[uiStage][iYPos - 1][iXPos]) { ucMap[iYPos - 1][iXPos] = ' '; } else { ucMap[iYPos - 1][iXPos] = '.'; } --iYPos; } } break;
case VK_DOWN: HbmHero = HbmBack;
if (' ' == ucMap[iYPos + 1][iXPos]) { ++iYPos; } else if ('.' == ucMap[iYPos + 1][iXPos]) { ++iYPos; } else if ('B' == ucMap[iYPos + 1][iXPos]) { if (' ' == ucMap[iYPos + 2][iXPos]) // [box] [box] { ucMap[iYPos + 2][iXPos] = ucMap[iYPos + 1][iXPos]; //박스 밀고
if ('.' != ucStageMap[uiStage][iYPos + 1][iXPos]) { ucMap[iYPos + 1][iXPos] = ' '; } else { ucMap[iYPos + 1][iXPos] = '.'; }
++iYPos; } else if ('.' == ucMap[iYPos + 2][iXPos]) { ucMap[iYPos + 2][iXPos] = ucMap[iYPos + 1][iXPos]; //박스 밀고
if ('.' != ucStageMap[uiStage][iYPos + 1][iXPos]) { ucMap[iYPos + 1][iXPos] = ' '; } else { ucMap[iYPos + 1][iXPos] = '.'; } ++iYPos; } }
break; |
리게임 시키기
해당판을 진행하다 다시 이번 게임을 진행하고 싶을때의 처리를 해준다. Home키를 눌렀을 때 다시 게임을 진행할 수 있도록 만들어 준다.
case VK_HOME: iRet = MessageBox(hWnd, "RE??", "RE??", MB_YESNO); if (IDYES == iRet) { InvalidateRect(hWnd, NULL, TRUE); LoadMap(); } return 0; |
게임의 클리어 처리하기
게임의 클리어는 맵 안의 모든점 위에 박스가 올라가면 클리어라고 정의한다. 이를 판별하기 위해서 현재 맵의 상자의 위치가 기존 맵의 점과 같으면 된다.(점의 위치는 항상 변하지 않는다.) 그러므로 새로 변수를 만들어 맵을 그릴 때 점의 갯수를 카운트 하고, 게임 이동이동마다 항상 박스와 점이 겹치는 수를 카운트해서 둘이 일치하면 게임 클리어가 된다.
그리고 모든 판을 클리어 하였을 때 모든 판을 클리어 하였다고 메세지를 띄운 뒤 게임을 종료하도록 만들어야 한다.
ON_KEYDOWN 함수 마지막에서.... uiDotCount = 0;
for (iYCount = 0; iYCount < YFRAME; iYCount++) { for (iXCount = 0; iXCount < XFRAME; iXCount++) { if ('B' == ucMap[iYCount][iXCount]) { if ('.' == ucStageMap[uiStage][iYCount][iXCount]) { ++uiDotCount; } }
} }
if (uiDotCount == uiDotNum) { MessageBox(hWnd, "Clear", "Clear", MB_OK); uiStage++;
if (STAGE <= uiStage) { MessageBox(hWnd, "Mission All Clear", "Mission All Clear", MB_OK); SendMessage(hWnd, WM_DESTROY, 0, 0); return 0; }
InvalidateRect(hWnd, NULL, TRUE); LoadMap();
} |
히어로 이동에 따른 무효화 영역 설정
히어로가 이동함에 따라서 모든 맵을 다시 그린다면 엄청난 자원의 낭비를 초래하게 된다. 히어로의 움직임에 따라서 특정영역만 다시 그리게 한다면 많은 이익을 얻을 수 있다. RECT구조체를 사용하여 특정 영역만 무효화할 수 있도록 해보자.
영역 설정 초기화
RECT stArea; //left, top, Right, bottom
stArea.left = iXPos * XTILE; stArea.top = iYPos * YTILE; stArea.right = (iXPos + 1) * XTILE;
stArea.bottom = (iYPos + 1) *YTILE; |
이동에 따른 무효화 영역 변경
LEFT의 경우 히어로 자신의 현재 자리와 그 오른쪽영역만 다시 그리면 된다. 그러므로 아래와 같은 처리를 해주고, 박스가 존재할 경우 박스도 같이 이동을 시켜 주어야 하므로 이 또한 처리를 해준다. 이와 같은 방법으로 4방향을 모두 처리하면 되겠다.
case VK_LEFT: uiScore++; HbmHero = HbmLeft; if (' ' == ucMap[iYPos][iXPos - 1]) { --iXPos; stArea.left = iXPos * XTILE; } else if ('.' == ucMap[iYPos][iXPos - 1]) { --iXPos; stArea.left = iXPos * XTILE; } else if ('B' == ucMap[iYPos][iXPos - 1]) { if (' ' == ucMap[iYPos][iXPos - 2]) // [box] [box] { ucMap[iYPos][iXPos - 2] = ucMap[iYPos][iXPos - 1]; //박스 밀고
if ('.' != ucStageMap[uiStage][iYPos][iXPos - 1]) { ucMap[iYPos][iXPos - 1] = ' '; } else { ucMap[iYPos][iXPos - 1] = '.'; } --iXPos; stArea.left = (iXPos - 1) * XTILE; } else if ('.' == ucMap[iYPos][iXPos - 2]) { ucMap[iYPos][iXPos - 2] = ucMap[iYPos][iXPos - 1]; //박스 밀고
if ('.' != ucStageMap[uiStage][iYPos][iXPos - 1]) { ucMap[iYPos][iXPos - 1] = ' '; } else { ucMap[iYPos][iXPos - 1] = '.'; } --iXPos; stArea.left = (iXPos - 1) * XTILE; } }
break; |
스코어 처리
푸시푸시는 얼마나 적은 움직임으로 게임을 클리어 하는 것이 관건인 게임이다. 그 움직임을 카운트 하여 더욱 재미를 사용자로 하여금 재미를 유발하여 보자.
방법은 간단하다. 이동키 마다 미리 선언된 변수를 1씩 증가만 시켜 주면 된다.
case VK_RIGHT: ..... ++uiScore;
case VK_LEFT: ..... uiScore++;
case VK_UP: ..... uiScore++;
case VK_DOWN: ..... uiScore++;
//윈도우 창에 표시(ON_PAINT 에서...)
wsprintf(cTitle, "Stage : %d, Key Count : %d", uiStage + 1, uiScore); SetWindowText(hWnd, cTitle); |
결과
내일 할 일
다듬기( 코드 최적화, UI 새로 스무스하게 그리기)