본문으로 바로가기

20151119 - 매핑기법, 주인공의 이동

category 마이 스토리/내용 정리중인 글들.. 2015. 11. 19. 15:50



맵을 만드는 수업 중이다. 맵이 완성되었고 소스는,


#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);
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;

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);
  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));

  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)
    {
      if (ucMap[iYCnt][iXCnt] == '#')
      {
        SelectObject(memDC, hbmGround);
      }
      else if (ucMap[iYCnt][iXCnt] == ' ')
      {
        SelectObject(memDC, hbmRoad);
      }
      BitBlt(hdc, (iXCnt * X_TILE), (iYCnt * Y_TILE), X_TILE, Y_TILE, memDC, 00, SRCCOPY);
    }
  }

  /*히어로 그리기*/
  SelectObject(memDC, hbmHero);
  BitBlt(hdc, iXPos, iYPos, 4848, memDC, 00, SRCCOPY);
  
  EndPaint(hWnd, &ps);
  
  return 0;
}

LRESULT On_KeyDown(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
  switch (wParam) 
  {
    case VK_LEFT:
      hbmHero = hbmLeft;
      iXPos = iXPos - X_MOVE;
      break;

    case VK_RIGHT:
      hbmHero = hbmRight;
      iXPos = iXPos + X_MOVE;  
      break;

    case VK_UP:
      hbmHero = hbmBack;
      iYPos = iYPos - Y_MOVE;
      break;

    case VK_DOWN:
      hbmHero = hbmFront;
      iYPos = iYPos + Y_MOVE;
      break;
  }

  InvalidateRect(hWnd, NULL, TRUE);

  return 0;
}


이러하며,

실행화면은



이러하다.

이 게임은 벽과 길이 나눠져 있기에 따로 경계검사를 할 필요가 없다. 현재 매핑 기법을 활용하여 맵과 길을 나눠서 이중 포문으로 if - else if 로 조건을 걸어

그렸다. 


/*맵 그리기*/
  hdc = BeginPaint(hWnd, &ps);
  for (iYCnt = 0; iYCnt < Y_FRAME; ++iYCnt)
  {
    for (iXCnt = 0; iXCnt < X_FRAME; ++iXCnt)
    {
      if (ucMap[iYCnt][iXCnt] == '#')
      {
        SelectObject(memDC, hbmGround);
      }
      else if (ucMap[iYCnt][iXCnt] == ' ')
      {
        SelectObject(memDC, hbmRoad);
      }
      BitBlt(hdc, (iXCnt * X_TILE), (iYCnt * Y_TILE), X_TILE, Y_TILE, memDC, 00, SRCCOPY);
    }
  }


이 부분이다. 맵 자체가 2차원 이기에 2차원으로 직접 우리가 맵을 전역에

UCHAR ucMap[Y_FRAME][X_FRAME + 1=
{
  { "###############" },
  { "###############" },
  { "###  #####  ###" },
  { "###  #####  ###" },
  { "###  #####  ###" },
  { "###         ###" },
  { "###  #####  ###" },
  { "###  #####  ###" },
  { "###############" },
  { "###############" },
};


이러한 형태로 그렸고, 이렇게 매핑 기법을 활용해 우리가 얻는 이점은 쉽게 if 문으로 조건을 걸어 

      if (ucMap[iYCnt][iXCnt] == '#')
      {
        SelectObject(memDC, hbmGround);
      }
      else if (ucMap[iYCnt][iXCnt] == ' ')
      {
        SelectObject(memDC, hbmRoad);
      }

맵을 그리고 표현하는 것이 손 쉬워지고 직접 컴퓨터에게

이중 포문으로 매번 그리기를 시켜 속도가 저하되는 것을 막아준다.

매핑 기법에 대해선 좀 더 연구를 해보자.


map, or mapping ; 매핑, 사상(寫像)
다른 데이터 셋과 대응 관계를 가지고 있는 일련의 데이터 셋을 지칭한다.
메모리나 디스크 상에 현재 저장되어 있는 데이터나 객체 목록을 지칭한다.(2차원적인 변수에 맵을 직접 그려 저장해 놓은 데이터를 말하는 듯 하다.)
디스크 드라이브에 경로나 디스크 문자 (A:, C:, R: 등)를 할당하는 것으로, 특히 드라이브 매핑이라고 부르기도 한다.
일련의 객체들을 한 장소에서 다른 곳으로 이동시키는 것이다. 예를 들면, 디스크 상의 프로그램 모듈들은 메모리에 사상(寫像)된다. 메모리 내의 그래픽 이미지는 비디오 화면상에 사상된다. 하나의 주소는 다른 주소에 사상된다. 논리적 데이터베이스 구조는 물리적 데이터베이스에 사상된다. 매핑을 위해서는 대체로 한 형식에서 다른 형식으로 변환하는 것이 필요하다.
일련의 객체들을 다른 객체들에 관련시키는 것이다. 예를 들어, 어떤 공급자의 프로토콜은 OSI 참조 모델에 사상된다.




이젠 맵에다가 캐릭터를 같이 접어 넣는다.





#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);
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;

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);
  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));

  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)
    {
      if (ucMap[iYCnt][iXCnt] == '#')      //맵
      {
        SelectObject(memDC, hbmGround);
      }
      else if (ucMap[iYCnt][iXCnt] == ' ')  //길
      {
        SelectObject(memDC, hbmRoad);
      }
      else if (ucMap[iYCnt][iXCnt] == '@')  //히어로 -> 골뱅이 좌표값을 지워버리니까 안 움직이게 된다.
      {
        SelectObject(memDC, hbmHero);
      }
      BitBlt(hdc, (iXCnt * X_TILE), (iYCnt * Y_TILE), X_TILE, Y_TILE, memDC, 00, SRCCOPY);
    }
  }
  
  EndPaint(hWnd, &ps);
  
  return 0;
}

LRESULT On_KeyDown(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
  switch (wParam) 
  {
    case VK_LEFT:
      hbmHero = hbmLeft;
      iXPos = iXPos - X_MOVE;
      break;

    case VK_RIGHT:
      hbmHero = hbmRight;
      iXPos = iXPos + X_MOVE;  
      break;

    case VK_UP:
      hbmHero = hbmBack;
      iYPos = iYPos - Y_MOVE;
      break;

    case VK_DOWN:
      hbmHero = hbmFront;
      iYPos = iYPos + Y_MOVE;
      break;
  }

  InvalidateRect(hWnd, NULL, TRUE);

  return 0;
}






그래서 맵 변수 추가 해서 맵을 로드하며 현재 @ 의 즉 히어로의 위치를 검색도 같이 한다.

맴카피를 쓰지 않는 이유는 현재 소스의 히어로의 멈춰있는 @ 이 위치를 계속 로드하면서 같이 히어로의 위치를 찾으며 가져오기 위함이다.







#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);
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;  //현재 몇판인지 기억.
//스테이지
UCHAR ucStageMap[STAGE][Y_FRAME][X_FRAME + 1=
{
  {
    { "###############" },
    { "###############" },
    { "###  #####  ###" },
    { "###  #####  ###" },
    { "###  #####  ###" },
    { "###         ###" },
    { "###  #####  ###" },
    { "### @#####  ###" },
    { "###############" },
    { "###############" },
  },
  {
    { "###############" },
    { "###############" },
    { "######   ######" },
    { "####       ####" },
    { "###   ###   ###" },
    { "###   ###   ###" },
    { "####       ####" },
    { "####@#   ######" },
    { "###############" },
    { "###############" },
  },
};
//실제 맵
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);
  DeleteDC(memDC);
  
  PostQuitMessage(0);
  
  return 0;
}

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

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

  LoadMap();

  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));

  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)
    {
      if (ucMap[iYCnt][iXCnt] == '#')      //맵
      {
        SelectObject(memDC, hbmGround);
      }
      else if (ucMap[iYCnt][iXCnt] == ' ')  //길
      {
        SelectObject(memDC, hbmRoad);
      }
      else if (ucMap[iYCnt][iXCnt] == '@')  //히어로 -> 골뱅이 좌표값을 지워버리니까 안 움직이게 된다.
      {
        SelectObject(memDC, hbmHero);
      }
      BitBlt(hdc, (iXCnt * X_TILE), (iYCnt * Y_TILE), X_TILE, Y_TILE, memDC, 00, SRCCOPY);
    }
  }
  
  EndPaint(hWnd, &ps);
  
  return 0;
}

LRESULT On_KeyDown(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
  switch (wParam) 
  {
    case VK_LEFT:
      hbmHero = hbmLeft;
      iXPos = iXPos - X_MOVE;
      break;

    case VK_RIGHT:
      hbmHero = hbmRight;
      iXPos = iXPos + X_MOVE;  
      break;

    case VK_UP:
      hbmHero = hbmBack;
      iYPos = iYPos - Y_MOVE;
      break;

    case VK_DOWN:
      hbmHero = hbmFront;
      iYPos = iYPos + Y_MOVE;
      break;
  }

  InvalidateRect(hWnd, NULL, TRUE);

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

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

  return;
}





우리 스타크래프트 보면 다 감시 하는 것이다. 컴퓨터는 맵핵을 가지고 있다라는 것이다 한마디로.

다 이러한 방식으로 되어있다.


##을 전부 벽으로 만들고 단지 그리는 부분에 그 맵만 그려라. 라고 소스를 만들어 놨기 때문에

@의 위치를 출력하는 것을 따로 만들어 줘야 한다. 방향키 있는 곳으로 가서 바꿔보자.







#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;  //현재 몇판인지 기억.
//스테이지
UCHAR ucStageMap[STAGE][Y_FRAME][X_FRAME + 1=
{
  {
    { "###############" },
    { "###############" },
    { "###  #####  ###" },
    { "###  #####  ###" },
    { "###  #####  ###" },
    { "###         ###" },
    { "###  #####  ###" },
    { "### @#####  ###" },
    { "###############" },
    { "###############" },
  },
  {
    { "###############" },
    { "###############" },
    { "######   ######" },
    { "####       ####" },
    { "###   ###   ###" },
    { "###   ###   ###" },
    { "####       ####" },
    { "####@#   ######" },
    { "###############" },
    { "###############" },
  },
};
//실제 맵
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);
  DeleteDC(memDC);
  
  PostQuitMessage(0);
  
  return 0;
}

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

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

  LoadMap();

  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));

  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)
    {
      if (ucMap[iYCnt][iXCnt] == '#')      //맵
      {
        SelectObject(memDC, hbmGround);
      }
      else if (ucMap[iYCnt][iXCnt] == ' ')  //길
      {
        SelectObject(memDC, hbmRoad);
      }
      else if (ucMap[iYCnt][iXCnt] == '@')  //히어로 -> 골뱅이 좌표값을 지워버리니까 안 움직이게 된다.
      {
        SelectObject(memDC, hbmHero);
      }
      BitBlt(hdc, (iXCnt * X_TILE), (iYCnt * Y_TILE), X_TILE, Y_TILE, memDC, 00, SRCCOPY);
    }
  }
  
  EndPaint(hWnd, &ps);
  
  return 0;
}

LRESULT On_KeyDown(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
  switch (wParam) 
  {
    case VK_LEFT:
      hbmHero = hbmLeft;
      ucMap[iYPos][iXPos] = ' ';//원래의 골뱅이를 지우기.
      --iXPos;
      ucMap[iYPos][iXPos] = '@';
      break;

    case VK_RIGHT:
      hbmHero = hbmRight;
      //ucMap이 실제로 '@'의 좌표값을 가지고 있으므로,
      ucMap[iYPos][iXPos] = ' ';//원래의 골뱅이를 지우기.
      ++iXPos;
      ucMap[iYPos][iXPos] = '@';
      break;

    case VK_UP:
      hbmHero = hbmBack;
      ucMap[iYPos][iXPos] = ' ';//원래의 골뱅이를 지우기.
      --iYPos;
      ucMap[iYPos][iXPos] = '@';
      break;

    case VK_DOWN:
      hbmHero = hbmFront;
      ucMap[iYPos][iXPos] = ' ';//원래의 골뱅이를 지우기.
      ++iYPos;
      ucMap[iYPos][iXPos] = '@';
      break;
  }

  InvalidateRect(hWnd, NULL, TRUE);

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

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

  return;
}






현재 길을 다 지우고 돌아댕기게 된다.


스타크래프트의 맵 만들기에 보면 이와 같은 기법이 활용된다라는 것이다. 맵의 돌맹이는 #, 나무 ^ 뭐 이렇게 값들이 전부 정해져있고

우리는 매핑을 하는 것이다.


가장 조율을 기가막히게 했던 스타 1 만큼 스타 2는 인기를 못끌고 있는 것이다.

디아블로 같은 것도 마찬가지다.




푸시푸시는 상자를 저 원하는 위치에 상자를 갖다 놔야 한다.

즉 골뱅이가 있던 자리가 길이 될수도 있고 닷이 될 수도 있다.





#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;  //현재 몇판인지 기억.
//스테이지
UCHAR ucStageMap[STAGE][Y_FRAME][X_FRAME + 1=
{
  {
    { "###############" },
    { "###############" },
    { "###  #####  ###" },
    { "###  #####  ###" },
    { "###  #####  ###" },
    { "###   .     ###" },
    { "###  #####  ###" },
    { "### @#####  ###" },
    { "###############" },
    { "###############" },
  },
  {
    { "###############" },
    { "###############" },
    { "######   ######" },
    { "####  .    ####" },
    { "###   ###   ###" },
    { "###   ###   ###" },
    { "####       ####" },
    { "####@#   ######" },
    { "###############" },
    { "###############" },
  },
};
//실제 맵
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);
  DeleteDC(memDC);
  
  PostQuitMessage(0);
  
  return 0;
}

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

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

  LoadMap();

  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));

  ReleaseDC(hWnd, hdc);

  hbmHero = hbmFront;

  return 0;
}

LRESULT On_KeyDown(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
  switch (wParam) 
  {
    case VK_LEFT:
      hbmHero = hbmLeft;
      ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];
      --iXPos;
      ucMap[iYPos][iXPos] = '@';
      break;

    case VK_RIGHT:
      hbmHero = hbmRight;
      //ucMap이 실제로 '@'의 좌표값을 가지고 있으므로,
      ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];//현재 스테이지의 맵 정보를 그대로 들고온다, 허나 이렇게 된다면 원래 스테이지의 @ 위치에 히어로가 위치하면 클론을 만들어 버린다.
      ++iXPos;
      ucMap[iYPos][iXPos] = '@';
      break;

    case VK_UP:
      hbmHero = hbmBack;
      ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];
      --iYPos;
      ucMap[iYPos][iXPos] = '@';
      break;

    case VK_DOWN:
      hbmHero = hbmFront;
      ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];
      ++iYPos;
      ucMap[iYPos][iXPos] = '@';
      break;
  }

  InvalidateRect(hWnd, NULL, TRUE);

  return 0;
}

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

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

  return;
}









   case VK_RIGHT:
      hbmHero = hbmRight;
      //ucMap이 실제로 '@'의 좌표값을 가지고 있으므로,
      ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];//현재 스테이지의 맵 정보를 그대로 들고온다, 허나 이렇게 된다면 원래 스테이지의 @ 위치에 히어로가 위치하면 클론을 만들어 버린다.
      ++iXPos;
      ucMap[iYPos][iXPos] = '@';
      break;


이렇게 하면 스테이지의 @ 위치에 클론이 생겨나 버린다.



즉, 여태까지 했던것을 정리해보자면,


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

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

  return;
}

이부분의  ucStageMap 은 실제 스테이지의 기본 상태, 즉 영구적인 맵의 초기 상태를 가지고 있으며,

변하는 값들은 ucMap이 가지게 된다. ucMap이 Hero 위치 값이나 Box 위치 값들이 바뀌는 곳을 가지고 있는 것이다.

그렇기에 On_Paint에 이 변한 값들을 가지고 있는 ucMap을 출력하기만 한다면 된다. 또한, 방향키를 보면


LRESULT On_KeyDown(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
  switch (wParam) 
  {
    case VK_LEFT:
      hbmHero = hbmLeft;
      ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];
      --iXPos;
      ucMap[iYPos][iXPos] = '@';
      break;

    case VK_RIGHT:
      hbmHero = hbmRight;
      //ucMap이 실제로 '@'의 좌표값을 가지고 있으므로,
      ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];//현재 스테이지의 맵 정보를 그대로 들고온다, 허나 이렇게 된다면 원래 스테이지의 @ 위치에 히어로가 위치하면 클론을 만들어 버린다.
      ++iXPos;
      ucMap[iYPos][iXPos] = '@';
      break;

    case VK_UP:
      hbmHero = hbmBack;
      ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];
      --iYPos;
      ucMap[iYPos][iXPos] = '@';
      break;

    case VK_DOWN:
      hbmHero = hbmFront;
      ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];
      ++iYPos;
      ucMap[iYPos][iXPos] = '@';
      break;
  }

  InvalidateRect(hWnd, NULL, TRUE);

  return 0;
}

여기서


ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];


이와 같이 매 움직임이 시작하기 전에 현재 기본 맵의 초기 상태를 가지고 있는 ucStageMap 변수를 이용해 변하는 곳의 그 자리를 기본 맵의 초기 상태로

복원 시킨 뒤 움직이는 

      ++iXPos;
      ucMap[iYPos][iXPos] = '@';

소스 라인 들이다.

하지만 아직 완벽하지는 않다. 그 전 상태를 그대로 가지고 출력하므로 원래 @ 위치의 영웅 또한 그대로 남아있어 클론을 만들어 버린다.






막간을 이용해 배경 바꿨다.


이제

경계검사를 해보자. 경우으,ㅣ 수를 다 따져봐야 한다.

1. @가 있는데 옆에 길이다 .그럼 이동 가능.

2. @가 있는데 오른쪽에 닷 이다. 그럼 이동 가능.

3. @가 있는데 오른쪽에 박스가 있다. 그럼 경우의 수가 갈린다.

1. 상자 오른쪽에 길이면 이동 가능

2. 상자 오른쪽에 닷이면 이동 가능

3. 상자 오른쪽에 또 상자면 이동 불가능

4. 상자 오른쪽에 벽이면 이동 불가능


정리 하자면, 

@ + 길 == 가능

이런식이다.


이제 이 경우의 수를 어떻게 잘 조합할 것이냐가 문제.

원본 그림을 복사하는 거 까지는 좋았으나 이제 어떻게 해야 할 까,







#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;  //현재 몇판인지 기억.
//스테이지
UCHAR ucStageMap[STAGE][Y_FRAME][X_FRAME + 1=
{
  {
    { "###############" },
    { "###############" },
    { "###  #####  ###" },
    { "###  #####  ###" },
    { "###  #####  ###" },
    { "###   .     ###" },
    { "###  #####  ###" },
    { "### @#####  ###" },
    { "###############" },
    { "###############" },
  },
  {
    { "###############" },
    { "###############" },
    { "######   ######" },
    { "####  .    ####" },
    { "###   ###   ###" },
    { "###   ###   ###" },
    { "####       ####" },
    { "####@#   ######" },
    { "###############" },
    { "###############" },
  },
};
//실제 맵
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);
  DeleteDC(memDC);
  
  PostQuitMessage(0);
  
  return 0;
}

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

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

  LoadMap();

  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));

  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;
      }
      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);

  EndPaint(hWnd, &ps);
  
  return 0;
}

LRESULT On_KeyDown(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
  switch (wParam) 
  {
    case VK_LEFT:
      hbmHero = hbmLeft;
      ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];
      --iXPos;
      ucMap[iYPos][iXPos] = '@';
      break;

    case VK_RIGHT:
      hbmHero = hbmRight;
      //ucMap이 실제로 '@'의 좌표값을 가지고 있으므로,
      ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];
      ++iXPos;
      ucMap[iYPos][iXPos] = '@';
      break;

    case VK_UP:
      hbmHero = hbmBack;
      ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];
      --iYPos;
      ucMap[iYPos][iXPos] = '@';
      break;

    case VK_DOWN:
      hbmHero = hbmFront;
      ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];
      ++iYPos;
      ucMap[iYPos][iXPos] = '@';
      break;
  }

  InvalidateRect(hWnd, NULL, TRUE);

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

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

  return;
}






이 부분 수정 


/*맵 그리기*/
  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;
      }
      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);

  EndPaint(hWnd, &ps);



이 부분 수정


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




논리식이 작은 걸로 하도록 하자.


박스이고 길이면 이동 가능

박스이고 벽이면 이동 불가


이런 논리식으로 진행된다. 이런걸 논리식이라 부르는 가 보다.

이 논리식을 만들어보자. 






#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;  //현재 몇판인지 기억.
//스테이지
UCHAR ucStageMap[STAGE][Y_FRAME][X_FRAME + 1=
{
  {
    { "###############" },
    { "###############" },
    { "###  #####  ###" },
    { "###  #####  ###" },
    { "###  ###    ###" },
    { "###   .  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);

  LoadMap();

  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));

  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);

  EndPaint(hWnd, &ps);
  
  return 0;
}

LRESULT On_KeyDown(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
  switch (wParam) 
  {
    case VK_LEFT:
      hbmHero = hbmLeft;
      ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];
      --iXPos;
      ucMap[iYPos][iXPos] = '@';
      break;

    case VK_RIGHT:
      hbmHero = hbmRight;
      //ucMap이 실제로 '@'의 좌표값을 가지고 있으므로,
      ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];
      if (ucMap[iYPos][iXPos] + 1 == '#')//이제 이동 시킬 조건이 여기 다 들어간다., 일단 벽이면.
      {
        break;
      }
      if (ucMap[iYPos][iXPos] + 1 == 'B')//상자면,
      {
        break;
      }
      ++iXPos;
      ucMap[iYPos][iXPos] = '@';
      break;

    case VK_UP:
      hbmHero = hbmBack;
      ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];
      --iYPos;
      ucMap[iYPos][iXPos] = '@';
      break;

    case VK_DOWN:
      hbmHero = hbmFront;
      ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];
      ++iYPos;
      ucMap[iYPos][iXPos] = '@';
      break;
  }

  InvalidateRect(hWnd, NULL, TRUE);

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

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

  return;
}



현재 박스 그림 추가하고 그림 출력시켰다.

여전히 방향키 쪽 경우의 수 if-else로 나누는 중이다.



현재 경우의 수로 나누는 중이며 방법을 조금 바꿨다. 갈 수 없다면 이 아닌 갈 수 있다면,

소스는,




#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;  //현재 몇판인지 기억.
//스테이지
UCHAR ucStageMap[STAGE][Y_FRAME][X_FRAME + 1=
{
  {
    { "###############" },
    { "###############" },
    { "###  #####  ###" },
    { "###  #####  ###" },
    { "###  ###    ###" },
    { "###   .  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);

  LoadMap();

  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));

  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);

  EndPaint(hWnd, &ps);
  
  return 0;
}

LRESULT On_KeyDown(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
  switch (wParam) 
  {
    case VK_LEFT:
      hbmHero = hbmLeft;
      ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];
      --iXPos;
      ucMap[iYPos][iXPos] = '@';
      break;

    case VK_RIGHT:
      hbmHero = hbmRight;

      ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];
      if (ucMap[iYPos][iXPos + 1== ' ')//이제 이동 시킬 조건이 여기 다 들어간다., 일단 길이면
      {
        ++iXPos;
      }
      else if (ucMap[iYPos][iXPos + 1== '.')//닷 이면
      {
        ++iXPos;
      }
      else if (ucMap[iYPos][iXPos + 1== 'B')//상자이고,
      {
        if (ucMap[iYPos][iXPos + 2== ' ')//길 이면
        {
          ++iXPos;
        }
        else if (ucMap[iYPos][iXPos + 2== '.')//닷 이면
        {
          ++iXPos;
        }
      }

      ucMap[iYPos][iXPos] = '@';
      break;

    case VK_UP:
      hbmHero = hbmBack;
      ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];
      --iYPos;
      ucMap[iYPos][iXPos] = '@';
      break;

    case VK_DOWN:
      hbmHero = hbmFront;
      ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];
      ++iYPos;
      ucMap[iYPos][iXPos] = '@';
      break;
  }

  InvalidateRect(hWnd, NULL, TRUE);

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

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

  return;
}


이러하며,




벽이면 막히고 닷이면 통과한다.

상자를 움직이는건 오후 수업때 부터 하기로 했다.












#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;  //현재 몇판인지 기억.
//스테이지
UCHAR ucStageMap[STAGE][Y_FRAME][X_FRAME + 1=
{
  {
    { "###############" },
    { "###############" },
    { "###  #####  ###" },
    { "###  #####  ###" },
    { "###  ###    ###" },
    { "###   .  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);

  EndPaint(hWnd, &ps);
  
  return 0;
}

LRESULT On_KeyDown(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
  switch (wParam) 
  {
    case VK_LEFT:
      hbmHero = hbmLeft;
      
      if (ucMap[iYPos][iXPos - 1== ' ')//이제 이동 시킬 조건이 여기 다 들어간다., 일단 길이면
      {
        --iXPos;
      }
      else if (ucMap[iYPos][iXPos - 1== '.')//닷 이면
      {
        --iXPos;
      }
      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;
        }
        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;
        }
      }
      //ucMap[iYPos][iXPos] = ucStageMap[uiStage][iYPos][iXPos];
      //ucMap[iYPos][iXPos] = '@';
      break;

    case VK_RIGHT:
      hbmHero = hbmRight;
      
      if (ucMap[iYPos][iXPos + 1== ' ')//이제 이동 시킬 조건이 여기 다 들어간다., 일단 길이면
      {
        ++iXPos;
      }
      else if (ucMap[iYPos][iXPos + 1== '.')//닷 이면
      {
        ++iXPos;
      }
      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;
        }
        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;
        }
      }

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

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

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

  InvalidateRect(hWnd, NULL, TRUE);

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

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

  return;
}




현재 히어로 - 박스 - 닷 을 만나면 까지 전부 처리를 해 놓은 상태이다.

실행화면은,









변수를 추가하여 . 넘버링을 한 다음 그 넘버링이 전부 채워 졌다면 , 을 위해 카운트 변수를 추가하여 세면서 같아지는지 확인한다.

#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;

//스테이지
UCHAR ucStageMap[STAGE][Y_FRAME][X_FRAME + 1=
{
  {
    { "###############" },
    { "###############" },
    { "###  #####  ###" },
    { "###  #####  ###" },
    { "###  ###    ###" },
    { "###   .  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);

  EndPaint(hWnd, &ps);
  
  return 0;
}

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

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

    case VK_RIGHT:
      hbmHero = hbmRight;
      
      if (ucMap[iYPos][iXPos + 1== ' ')//이제 이동 시킬 조건이 여기 다 들어간다., 일단 길이면
      {
        ++iXPos;
      }
      else if (ucMap[iYPos][iXPos + 1== '.')//닷 이면
      {
        ++iXPos;
      }
      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;
        }
        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;
        }
      }

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

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

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

  InvalidateRect(hWnd, NULL, TRUE);

  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;

  //맴 카피를 쓰지 않고 이중 포문을 사용하는 이유는 골뱅이의 위치 때문이다.
  //즉, 골뱅이의 좌표를 알 수 있도록 맴카피를 쓰지 않고 맵을 로드하며 직접 @의 위치를 찾는 것이다.
  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;
}




완성된 소스는 이러하며,




잘 뜬다.

이제 아마 스테이지 이동하는 것을 할 듯 싶다.


잘되는지 닷과 박스를 맵에 추가해서 다시 해보자.





히어로 혼자는 다시 그려져야 한다라는 것이다

픽셀좌표가 아닌 카운터 좌표를 쓰고 있는 것이다.

가로 48 세로 48 이지만 x, y 값인 각각 47이다.


키가 눌려지면 막혀서 안움직이더라도 캐릭터자체는 다시 계속






그림을 다시 그려주기 위해 rect를