본문으로 바로가기

게임 실행시..


1. 메모리에 각각 비트맵을 LoadBitmap() 를 이용해 핸들러 값을 각 비트맵 변수에 담아둔다.

2. 다음 우리가 정의한 함수인 LoadMap()를 호출하여 우리가 정의한 맵 값인 영구적인, 불변의 맵 값인 ucStageMap의 맵 값들을

가변적으로 이용할 맵 변수인 ucMap 에 2중 for문을 활용하여 넣는다. 하지만 왜 memcpy() 로 간단히 안 넣었을까.

이유는,

1. 우리가 정의한 맵 안의 '.' 최대 갯수도 세아려야 하고,

2. 주인공의 표시인 '@' 값도 그 위치 값만 들고오고 '@' 값은 읽어들이면 그 부분이 불변의 값이 되므로 클론이 만들어 지므로

안된다.

3. 히어로의 기본적인 그림 상태를 hbmHero = hbmFront; 로 설정해 두고 윈도우 선 처리 작업을 마친다.

4. On_Paint() 로 가서 기본적인 페인팅 선 처리를 자동적으로 작업하게 되는데, 그리는 것은 당연히 영구적인 값이 아닌 가변적인 맵 값, 즉,

   주인공의 값인 '@' 가 지워져 그 영웅의 기본적인 좌표값만 가지고 있는 ucMap 으로 화면의 맵을 출력하게 되는데

[ '#' : 벽 , ' ' : 길, '.' : 닷(상자를 넣어야 하는 목적지), 'B' : 박스 ] 이처럼 각기 다른 현재 값을 읽어들어 SelectObject() 함수로 

현재 비트맵 그림들이 저장되어있는 메모리 공간인 memDC에 가서 아까 저장한 핸들러 값으로 해당 비트맵을 선택해준다.

5. 그런다음 BitBlt() 로 화면에 뿌리는데 


BitBlt(그림 x 좌표, 그림 y 좌표, 그림 넓이, 그림 높이, 그림 그려진 메모리 DC, 그림 시작 x 좌표, 그림 시작 y 좌표, 스타일);


이와 같으니 


BitBlt(hdc, (iXCnt * X_TILE), (iYCnt * Y_TILE), X_TILE, Y_TILE, memDC, 0, 0, SRCCOPY);


이와 같이 설정해 주면된다.















이번엔 스코어 추가.

게임 다시 시작 키를 추가할 것이다.


#include <windows.h>
#include "resource.h"
#include "Smart.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPSTR lpszClass = L"PushPushGame";

LRESULT On_Destroy(HWND, WPARAM, LPARAM);
LRESULT On_Create(HWND hWnd, WPARAM wParam, LPARAM lParam);
LRESULT On_Paint(HWND hWnd, WPARAM wParam, LPARAM lParam);
LRESULT On_KeyDown(HWND hWnd, WPARAM wParam, LPARAM lParam);
void LoadMap();
StMsgMap msgMap[] = 
{
  { WM_DESTROY, On_Destroy },
  { WM_CREATE, On_Create },
  { WM_PAINT, On_Paint },
  { WM_KEYDOWN, On_KeyDown },
  { WM_NULL, 0 }
};

static HBITMAP hbmFront;
static HBITMAP hbmBack;
static HBITMAP hbmLeft;
static HBITMAP hbmRight;

static HBITMAP hbmGround;
static HBITMAP hbmRoad;
static HBITMAP hbmHero;
static HBITMAP hbmBox;
static HBITMAP hbmDot;

HDC memDC;

/*현재 히어로의 위치 -> '@'*/
static int iXPos;
static int iYPos;

unsigned int uiStage;  //현재 몇판인지 기억.

unsigned int uiDotNum;  //현재 맵에 
unsigned int uiDotCount;

unsigned int uiScore;

WCHAR cTitle[200];
//스테이지
UCHAR ucStageMap[STAGE][Y_FRAME][X_FRAME + 1=
{
  {
    { "###############" },
    { "###############" },
    { "###         ###" },
    { "###   B     ###" },
    { "###   .     ###" },
    { "###   .  B  ###" },
    { "###   .  B  ###" },
    { "### @       ###" },
    { "###############" },
    { "###############" },
  },
  {
    { "###############" },
    { "###############" },
    { "######   ######" },
    { "####  .    ####" },
    { "###   ##    ###" },
    { "###   ##  B ###" },
    { "####        ###" },
    { "####@    ######" },
    { "###############" },
    { "###############" },
  },
};
//실제 맵
UCHAR ucMap[Y_FRAME][X_FRAME + 1];  

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
{
  HWND hWnd;
  MSG Message;
  WNDCLASS WndClass;
  g_hInst = hInstance;

  WndClass.cbClsExtra = 0;
  WndClass.cbWndExtra = 0;
  WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
  WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
  WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  WndClass.hInstance = hInstance;
  WndClass.lpfnWndProc = (WNDPROC)WndProc;
  WndClass.lpszClassName = lpszClass;
  WndClass.lpszMenuName = NULL;
  WndClass.style = CS_HREDRAW | CS_VREDRAW;
  RegisterClass(&WndClass);
  //X_WIN_SIZE, Y_WIN_SIZE 로 창이 어디에 뜨는지 설정.
  hWnd = CreateWindow
    (
      lpszClass,        // 윈도우의 클래스를 지정하는 문자열
      lpszClass,        // 윈도우의 타이틀 바 이름
      WS_OVERLAPPEDWINDOW,  // 윈도우의 형태를 지정, WS_OVERLAPPEDWINDOW 은 가장 무난한 형태의 윈도우 설정 상태
      CW_USEDEFAULT,      // 윈도우의 크기와 위치를 지정, x, CW_USEDEFAULT 은 적당한 크기와 위치를 설정, 차일드 윈도우는 부모 윈도우의 좌상단을 기준
      CW_USEDEFAULT,      // y
      X_WIN_SIZE,        // 차일드 윈도우의 창 크기 설정, x
      Y_WIN_SIZE,        // y
      NULL, (HMENU)NULL, hInstance, NULL  // 이건 나중에..
    );
  ShowWindow(hWnd, nCmdShow);

  while (GetMessage(&Message, 000)) 
  {
    TranslateMessage(&Message);
    DispatchMessage(&Message);
  }
  return Message.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
  StMsgMap *stpMap = msgMap;

  /*asm 때 했던 방식 그대로 활용, switch-case 를 함수포인터로 표현. - 이렇게 한다면 공유하는 변수들을 전역에 가지고 있어야 한다.*/
  while ((*stpMap).uiMsg != WM_NULL)
  {
    if (iMessage == (*stpMap).uiMsg)
    {
      ((*stpMap).fp)(hWnd, wParam, lParam);

      return 0;
    }

    ++stpMap;
  }

  /*
  switch (iMessage)
  {
  case WM_CREATE:
  hbmFront = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP_CHAR_FRONT));
  hbmBack = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP_CHAR_BACK));
  hbmLeft = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP_CHAR_LEFT));
  hbmRight = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP_CHAR_RIGHT));
  hbmGround = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP_BACKGROUND));
  return 0;

  case WM_DESTROY:
  DeleteObject(hbmFront);
  DeleteObject(hbmBack);
  DeleteObject(hbmLeft);
  DeleteObject(hbmRight);
  DeleteObject(hbmGround);
  PostQuitMessage(0);
  return 0;
  }
  */


  return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}

LRESULT On_Destroy(HWND hWnd, WPARAM wParam, LPARAM lParam)
{  
  DeleteObject(hbmFront);
  DeleteObject(hbmBack);
  DeleteObject(hbmLeft);
  DeleteObject(hbmRight);
  DeleteObject(hbmGround);
  DeleteObject(hbmRoad);
  DeleteObject(hbmDot);
  DeleteObject(hbmBox);
  DeleteDC(memDC);
  
  PostQuitMessage(0);
  
  return 0;
}

LRESULT On_Create(HWND hWnd, WPARAM wParam, LPARAM lParam)
{  
  HDC hdc;

  hdc = GetDC(hWnd);
  memDC = CreateCompatibleDC(hdc);

  hbmFront = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP_CHAR_FRONT));
  hbmBack = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP_CHAR_BACK));
  hbmLeft = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP_CHAR_LEFT));
  hbmRight = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP_CHAR_RIGHT));
  hbmGround = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP_BACKGROUND));
  hbmRoad = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP_ROAD));
  hbmDot = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP_DOT));
  hbmBox = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP_BOX));

  LoadMap();

  ReleaseDC(hWnd, hdc);

  hbmHero = hbmFront;

  return 0;
}

LRESULT On_Paint(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
  HDC hdc;
  PAINTSTRUCT ps;
  int iXCnt;
  int iYCnt;

  /*맵 그리기*/
  hdc = BeginPaint(hWnd, &ps);
  for (iYCnt = 0; iYCnt < Y_FRAME; ++iYCnt)
  {
    for (iXCnt = 0; iXCnt < X_FRAME; ++iXCnt)
    {
      switch (ucMap[iYCnt][iXCnt])
      {
        case '#':
          SelectObject(memDC, hbmGround);
          break;

        case ' ':
          SelectObject(memDC, hbmRoad);
          break;

        case '.':
          SelectObject(memDC, hbmDot);
          break;

        case 'B':
          SelectObject(memDC, hbmBox);
          break;
      }
      BitBlt(hdc, (iXCnt * X_TILE), (iYCnt * Y_TILE), X_TILE, Y_TILE, memDC, 00, SRCCOPY);
    }
  }

  SelectObject(memDC, hbmHero);
  BitBlt(hdc, (iXPos * X_TILE), (iYPos * Y_TILE), X_TILE, Y_TILE, memDC, 00, SRCCOPY);

  wsprintf(cTitle, L"STAGE : %d   KEYCOUNT : %d", uiStage + 1, uiScore);
  SetWindowText(hWnd, cTitle);
  EndPaint(hWnd, &ps);
  
  return 0;
}

LRESULT On_KeyDown(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
  int iXCnt;
  int iYCnt;
  RECT stArea;

  stArea.left = (iXPos * X_TILE);
  stArea.right = ((iXPos + 1) * X_TILE);
  stArea.top = (iYPos * Y_TILE);
  stArea.bottom = ((iYPos + 1) * Y_TILE);

  switch (wParam) 
  {
    case VK_LEFT:
      hbmHero = hbmLeft;
      ++uiScore;
      
      if (ucMap[iYPos][iXPos - 1== ' ')//이제 이동 시킬 조건이 여기 다 들어간다., 일단 길이면
      {
        --iXPos;
        stArea.left = (iXPos * X_TILE);
      }
      else if (ucMap[iYPos][iXPos - 1== '.')//닷 이면
      {
        --iXPos;
        stArea.left = (iXPos * X_TILE);
      }
      else if (ucMap[iYPos][iXPos - 1== 'B')//상자이고,
      {
        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) * X_TILE);
        }
        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) * X_TILE);
        }
      }
      //ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];
      //ucMap[iYPos][iXPos] = '@';
      break;

    case VK_RIGHT:
      hbmHero = hbmRight;
      ++uiScore;

      if (ucMap[iYPos][iXPos + 1== ' ')//이제 이동 시킬 조건이 여기 다 들어간다., 일단 길이면
      {
        ++iXPos;
        stArea.right = ((iXPos + 1) * X_TILE);
      }
      else if (ucMap[iYPos][iXPos + 1== '.')//닷 이면
      {
        ++iXPos;
        stArea.right = ((iXPos + 1) * X_TILE);
      }
      else if (ucMap[iYPos][iXPos + 1== 'B')//상자이고,
      {
        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.right = ((iXPos + 2) * X_TILE);
        }
        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.right = ((iXPos + 2) * X_TILE);
        }
      }

      //ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];
      //ucMap[iYPos][iXPos] = '@';
      break;

    case VK_UP:
      hbmHero = hbmBack;
      ++uiScore;
      
      if (ucMap[iYPos - 1][iXPos] == ' ')//이제 이동 시킬 조건이 여기 다 들어간다., 일단 길이면
      {
        --iYPos;
        stArea.top = (iYPos * Y_TILE);
      }
      else if (ucMap[iYPos - 1][iXPos] == '.')//닷 이면
      {
        --iYPos;
        stArea.top = (iYPos * Y_TILE);
      }
      else if (ucMap[iYPos - 1][iXPos] == 'B')//상자이고,
      {
        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;
          stArea.top = ((iYPos - 1) * Y_TILE);
        }
        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;
          stArea.top = ((iYPos - 1) * Y_TILE);
        }
      }
      //ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];
      //ucMap[iYPos][iXPos] = '@';
      break;

    case VK_DOWN:
      hbmHero = hbmFront;
      ++uiScore;
      
      if (ucMap[iYPos + 1][iXPos] == ' ')//이제 이동 시킬 조건이 여기 다 들어간다., 일단 길이면
      {
        ++iYPos;
        stArea.bottom = ((iYPos + 1) * Y_TILE);
      }
      else if (ucMap[iYPos + 1][iXPos] == '.')//닷 이면
      {
        ++iYPos;
        stArea.bottom = ((iYPos + 1) * Y_TILE);
      }
      else if (ucMap[iYPos + 1][iXPos] == 'B')//상자이고,
      {
        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;
          stArea.bottom = ((iYPos + 2) * Y_TILE);
        }
        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;
          stArea.bottom = ((iYPos + 2) * Y_TILE);
        }
      }
      //ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];
      //ucMap[iYPos][iXPos] = '@';
      break;

    case 'R':
      MessageBox(hWnd, L"R키 누름", L"Button", MB_OK);
      break;
  }

  InvalidateRect(hWnd, &stArea, FALSE);

  uiDotCount = 0;
  for (iYCnt = 0; iYCnt < Y_FRAME; ++iYCnt)
  {
    for (iXCnt = 0; iXCnt < X_FRAME; ++iXCnt)
    {
      if (ucMap[iYCnt][iXCnt] == 'B')
      {
        if (ucStageMap[uiStage][iYCnt][iXCnt] == '.')  // 두개가 일치한다면
        {
          ++uiDotCount;
        }
      }
    }

    if (uiDotCount == uiDotNum)
    {
      MessageBox(hWnd, TEXT("클리어 하셨습니다."), TEXT("Button"), MB_OK);
      break;
    }
  }

  return 0;
}
//맵 로드
void LoadMap()
{
  int iXCnt;
  int iYCnt;

  uiDotNum = 0;
  uiScore = 0;

  //맴 카피를 쓰지 않고 이중 포문을 사용하는 이유는 골뱅이의 위치 때문이다.
  //즉, 골뱅이의 좌표를 알 수 있도록 맴카피를 쓰지 않고 맵을 로드하며 직접 @의 위치를 찾는 것이다.
  for (iYCnt = 0; iYCnt < Y_FRAME; ++iYCnt)
  {
    for (iXCnt = 0; iXCnt < X_FRAME; ++iXCnt)
    {
      if (ucStageMap[uiStage][iYCnt][iXCnt] == '.')
      {
        ++uiDotNum;
      }

      if (ucStageMap[uiStage][iYCnt][iXCnt] == '@')
      {
        iYPos = iYCnt;
        iXPos = iXCnt;//현재 좌표 값에 넣는다.
        ucMap[iYCnt][iXCnt] == ' ';
        continue;
      }
      ucMap[iYCnt][iXCnt] = ucStageMap[uiStage][iYCnt][iXCnt];
    }
  }

  return;
}




현재 'HOME' 키를 누르면 다시 시작하겠냐고 묻게끔 메시지 박스를 띄우도록 했다.

이제 반환값 처리한다.



#include <windows.h>
#include "resource.h"
#include "Smart.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPSTR lpszClass = L"PushPushGame";

LRESULT On_Destroy(HWND, WPARAM, LPARAM);
LRESULT On_Create(HWND hWnd, WPARAM wParam, LPARAM lParam);
LRESULT On_Paint(HWND hWnd, WPARAM wParam, LPARAM lParam);
LRESULT On_KeyDown(HWND hWnd, WPARAM wParam, LPARAM lParam);
void LoadMap();
StMsgMap msgMap[] = 
{
  { WM_DESTROY, On_Destroy },
  { WM_CREATE, On_Create },
  { WM_PAINT, On_Paint },
  { WM_KEYDOWN, On_KeyDown },
  { WM_NULL, 0 }
};

static HBITMAP hbmFront;
static HBITMAP hbmBack;
static HBITMAP hbmLeft;
static HBITMAP hbmRight;

static HBITMAP hbmGround;
static HBITMAP hbmRoad;
static HBITMAP hbmHero;
static HBITMAP hbmBox;
static HBITMAP hbmDot;

HDC memDC;

/*현재 히어로의 위치 -> '@'*/
static int iXPos;
static int iYPos;

unsigned int uiStage;  //현재 몇판인지 기억.

unsigned int uiDotNum;  //현재 맵에 
unsigned int uiDotCount;

unsigned int uiScore;

WCHAR cTitle[200];
//스테이지
UCHAR ucStageMap[STAGE][Y_FRAME][X_FRAME + 1=
{
  {
    { "###############" },
    { "###############" },
    { "###         ###" },
    { "###   B     ###" },
    { "###   .     ###" },
    { "###   .  B  ###" },
    { "###   .  B  ###" },
    { "### @       ###" },
    { "###############" },
    { "###############" },
  },
  {
    { "###############" },
    { "###############" },
    { "######   ######" },
    { "####  .    ####" },
    { "###   ##    ###" },
    { "###   ##  B ###" },
    { "####        ###" },
    { "####@    ######" },
    { "###############" },
    { "###############" },
  },
};
//실제 맵
UCHAR ucMap[Y_FRAME][X_FRAME + 1];  

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
{
  HWND hWnd;
  MSG Message;
  WNDCLASS WndClass;
  g_hInst = hInstance;

  WndClass.cbClsExtra = 0;
  WndClass.cbWndExtra = 0;
  WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
  WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
  WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  WndClass.hInstance = hInstance;
  WndClass.lpfnWndProc = (WNDPROC)WndProc;
  WndClass.lpszClassName = lpszClass;
  WndClass.lpszMenuName = NULL;
  WndClass.style = CS_HREDRAW | CS_VREDRAW;
  RegisterClass(&WndClass);
  //X_WIN_SIZE, Y_WIN_SIZE 로 창이 어디에 뜨는지 설정.
  hWnd = CreateWindow
    (
      lpszClass,        // 윈도우의 클래스를 지정하는 문자열
      lpszClass,        // 윈도우의 타이틀 바 이름
      WS_OVERLAPPEDWINDOW,  // 윈도우의 형태를 지정, WS_OVERLAPPEDWINDOW 은 가장 무난한 형태의 윈도우 설정 상태
      CW_USEDEFAULT,      // 윈도우의 크기와 위치를 지정, x, CW_USEDEFAULT 은 적당한 크기와 위치를 설정, 차일드 윈도우는 부모 윈도우의 좌상단을 기준
      CW_USEDEFAULT,      // y
      X_WIN_SIZE,        // 차일드 윈도우의 창 크기 설정, x
      Y_WIN_SIZE,        // y
      NULL, (HMENU)NULL, hInstance, NULL  // 이건 나중에..
    );
  ShowWindow(hWnd, nCmdShow);

  while (GetMessage(&Message, 000)) 
  {
    TranslateMessage(&Message);
    DispatchMessage(&Message);
  }
  return Message.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
  StMsgMap *stpMap = msgMap;

  /*asm 때 했던 방식 그대로 활용, switch-case 를 함수포인터로 표현. - 이렇게 한다면 공유하는 변수들을 전역에 가지고 있어야 한다.*/
  while ((*stpMap).uiMsg != WM_NULL)
  {
    if (iMessage == (*stpMap).uiMsg)
    {
      ((*stpMap).fp)(hWnd, wParam, lParam);

      return 0;
    }

    ++stpMap;
  }

  /*
  switch (iMessage)
  {
  case WM_CREATE:
  hbmFront = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP_CHAR_FRONT));
  hbmBack = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP_CHAR_BACK));
  hbmLeft = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP_CHAR_LEFT));
  hbmRight = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP_CHAR_RIGHT));
  hbmGround = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP_BACKGROUND));
  return 0;

  case WM_DESTROY:
  DeleteObject(hbmFront);
  DeleteObject(hbmBack);
  DeleteObject(hbmLeft);
  DeleteObject(hbmRight);
  DeleteObject(hbmGround);
  PostQuitMessage(0);
  return 0;
  }
  */


  return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}

LRESULT On_Destroy(HWND hWnd, WPARAM wParam, LPARAM lParam)
{  
  DeleteObject(hbmFront);
  DeleteObject(hbmBack);
  DeleteObject(hbmLeft);
  DeleteObject(hbmRight);
  DeleteObject(hbmGround);
  DeleteObject(hbmRoad);
  DeleteObject(hbmDot);
  DeleteObject(hbmBox);
  DeleteDC(memDC);
  
  PostQuitMessage(0);
  
  return 0;
}

LRESULT On_Create(HWND hWnd, WPARAM wParam, LPARAM lParam)
{  
  HDC hdc;

  hdc = GetDC(hWnd);
  memDC = CreateCompatibleDC(hdc);

  hbmFront = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP_CHAR_FRONT));
  hbmBack = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP_CHAR_BACK));
  hbmLeft = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP_CHAR_LEFT));
  hbmRight = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP_CHAR_RIGHT));
  hbmGround = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP_BACKGROUND));
  hbmRoad = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP_ROAD));
  hbmDot = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP_DOT));
  hbmBox = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP_BOX));

  LoadMap();

  ReleaseDC(hWnd, hdc);

  hbmHero = hbmFront;

  return 0;
}

LRESULT On_Paint(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
  HDC hdc;
  PAINTSTRUCT ps;
  int iXCnt;
  int iYCnt;

  /*맵 그리기*/
  hdc = BeginPaint(hWnd, &ps);
  for (iYCnt = 0; iYCnt < Y_FRAME; ++iYCnt)
  {
    for (iXCnt = 0; iXCnt < X_FRAME; ++iXCnt)
    {
      switch (ucMap[iYCnt][iXCnt])
      {
        case '#':
          SelectObject(memDC, hbmGround);
          break;

        case ' ':
          SelectObject(memDC, hbmRoad);
          break;

        case '.':
          SelectObject(memDC, hbmDot);
          break;

        case 'B':
          SelectObject(memDC, hbmBox);
          break;
      }
      BitBlt(hdc, (iXCnt * X_TILE), (iYCnt * Y_TILE), X_TILE, Y_TILE, memDC, 00, SRCCOPY);
    }
  }

  SelectObject(memDC, hbmHero);
  BitBlt(hdc, (iXPos * X_TILE), (iYPos * Y_TILE), X_TILE, Y_TILE, memDC, 00, SRCCOPY);

  wsprintf(cTitle, L"STAGE : %d   KEYCOUNT : %d", uiStage + 1, uiScore);
  SetWindowText(hWnd, cTitle);
  EndPaint(hWnd, &ps);
  
  return 0;
}

LRESULT On_KeyDown(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
  int iXCnt;
  int iYCnt;
  RECT stArea;
  int iRet;

  stArea.left = (iXPos * X_TILE);
  stArea.right = ((iXPos + 1) * X_TILE);
  stArea.top = (iYPos * Y_TILE);
  stArea.bottom = ((iYPos + 1) * Y_TILE);

  switch (wParam) 
  {
    case VK_LEFT:
      hbmHero = hbmLeft;
      ++uiScore;
      
      if (ucMap[iYPos][iXPos - 1== ' ')//이제 이동 시킬 조건이 여기 다 들어간다., 일단 길이면
      {
        --iXPos;
        stArea.left = (iXPos * X_TILE);
      }
      else if (ucMap[iYPos][iXPos - 1== '.')//닷 이면
      {
        --iXPos;
        stArea.left = (iXPos * X_TILE);
      }
      else if (ucMap[iYPos][iXPos - 1== 'B')//상자이고,
      {
        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) * X_TILE);
        }
        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) * X_TILE);
        }
      }
      //ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];
      //ucMap[iYPos][iXPos] = '@';
      break;

    case VK_RIGHT:
      hbmHero = hbmRight;
      ++uiScore;

      if (ucMap[iYPos][iXPos + 1== ' ')//이제 이동 시킬 조건이 여기 다 들어간다., 일단 길이면
      {
        ++iXPos;
        stArea.right = ((iXPos + 1) * X_TILE);
      }
      else if (ucMap[iYPos][iXPos + 1== '.')//닷 이면
      {
        ++iXPos;
        stArea.right = ((iXPos + 1) * X_TILE);
      }
      else if (ucMap[iYPos][iXPos + 1== 'B')//상자이고,
      {
        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.right = ((iXPos + 2) * X_TILE);
        }
        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.right = ((iXPos + 2) * X_TILE);
        }
      }

      //ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];
      //ucMap[iYPos][iXPos] = '@';
      break;

    case VK_UP:
      hbmHero = hbmBack;
      ++uiScore;
      
      if (ucMap[iYPos - 1][iXPos] == ' ')//이제 이동 시킬 조건이 여기 다 들어간다., 일단 길이면
      {
        --iYPos;
        stArea.top = (iYPos * Y_TILE);
      }
      else if (ucMap[iYPos - 1][iXPos] == '.')//닷 이면
      {
        --iYPos;
        stArea.top = (iYPos * Y_TILE);
      }
      else if (ucMap[iYPos - 1][iXPos] == 'B')//상자이고,
      {
        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;
          stArea.top = ((iYPos - 1) * Y_TILE);
        }
        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;
          stArea.top = ((iYPos - 1) * Y_TILE);
        }
      }
      //ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];
      //ucMap[iYPos][iXPos] = '@';
      break;

    case VK_DOWN:
      hbmHero = hbmFront;
      ++uiScore;
      
      if (ucMap[iYPos + 1][iXPos] == ' ')//이제 이동 시킬 조건이 여기 다 들어간다., 일단 길이면
      {
        ++iYPos;
        stArea.bottom = ((iYPos + 1) * Y_TILE);
      }
      else if (ucMap[iYPos + 1][iXPos] == '.')//닷 이면
      {
        ++iYPos;
        stArea.bottom = ((iYPos + 1) * Y_TILE);
      }
      else if (ucMap[iYPos + 1][iXPos] == 'B')//상자이고,
      {
        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;
          stArea.bottom = ((iYPos + 2) * Y_TILE);
        }
        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;
          stArea.bottom = ((iYPos + 2) * Y_TILE);
        }
      }
      //ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];
      //ucMap[iYPos][iXPos] = '@';
      break;

    case VK_HOME:
      iRet = MessageBox(hWnd, L"다시 시작 하겠습니까?", L"Button", MB_YESNO);
      if (iRet == IDYES)
      {
        InvalidateRect(hWnd, NULL, TRUE);
        LoadMap();
      }
      
      return 0;
  }

  InvalidateRect(hWnd, &stArea, FALSE);

  uiDotCount = 0;
  for (iYCnt = 0; iYCnt < Y_FRAME; ++iYCnt)
  {
    for (iXCnt = 0; iXCnt < X_FRAME; ++iXCnt)
    {
      if (ucMap[iYCnt][iXCnt] == 'B')
      {
        if (ucStageMap[uiStage][iYCnt][iXCnt] == '.')  // 두개가 일치한다면
        {
          ++uiDotCount;
        }
      }
    }

    if (uiDotCount == uiDotNum)
    {
      MessageBox(hWnd, TEXT("클리어 하셨습니다."), TEXT("Button"), MB_OK);
      break;
    }
  }

  return 0;
}
//맵 로드
void LoadMap()
{
  int iXCnt;
  int iYCnt;

  uiDotNum = 0;
  uiScore = 0;

  //맴 카피를 쓰지 않고 이중 포문을 사용하는 이유는 골뱅이의 위치 때문이다.
  //즉, 골뱅이의 좌표를 알 수 있도록 맴카피를 쓰지 않고 맵을 로드하며 직접 @의 위치를 찾는 것이다.
  for (iYCnt = 0; iYCnt < Y_FRAME; ++iYCnt)
  {
    for (iXCnt = 0; iXCnt < X_FRAME; ++iXCnt)
    {
      if (ucStageMap[uiStage][iYCnt][iXCnt] == '.')
      {
        ++uiDotNum;
      }

      if (ucStageMap[uiStage][iYCnt][iXCnt] == '@')
      {
        iYPos = iYCnt;
        iXPos = iXCnt;//현재 좌표 값에 넣는다.
        ucMap[iYCnt][iXCnt] == ' ';
        continue;
      }
      ucMap[iYCnt][iXCnt] = ucStageMap[uiStage][iYCnt][iXCnt];
    }
  }

  return;
}














주의점은

case VK_HOME:
      iRet = MessageBox(hWnd, L"다시 시작 하겠습니까?", L"Button", MB_YESNO);
      if (iRet == IDYES)
      {
        InvalidateRect(hWnd, NULL, TRUE);
        LoadMap();
      }
      
      return 0;
  }


InvalidateRect(hWnd, NULL, TRUE);


이것으로 다시 그려줘야 한다.