본문 바로가기
코스웨어/15년 스마트컨트롤러

20151119_박서연_일일업무일지_WinAPI(6)

by 알 수 없는 사용자 2015. 11. 19.
728x90
반응형

2015-11-19


* WinAPI


#PUSH PUSH


전날 소스

 main.c


금일 개발 부분

맵 그리기

UCHAR ucStageMap[STAGE][YFRAME][XFRAME+1= 

{
  {
      "###############"
    , "#@            #"
    , "#             #"
    , "#             #"
    , "#   B.     .  #"
    , "#     .B   B  #"
    , "#    .        #"
    , "#    B        #"
    , "#             #"
    , "###############"
  },
  {
      "###############"
    , "#@      #######"
    , "######  #######"
    , "####    #######"
    , "#### ##    ####"
    , "####  #    #.##"
    , "####  # B  # ##"
    , "####  #      ##"
    , "###    ##    ##"
    , "###############"
  }
};
UCHAR ucMap[YFRAME][XFRAME + 1];

위와 같이 맵의 자료구조를 설정해 놓으면 판수에따른 다른 맵을 그릴 수 있다.


 void LoadMap(void)

{
  int iXCount;
  int iYCount;


  for (iYCount = 0; iYCount < YFRAME; iYCount++)
  {
    for (iXCount = 0; iXCount < XFRAME; iXCount++)
    {
      //히어로의 시작위치 잡아주기
      if ('@' == ucStageMap[uiStage][iYCount][iXCount])
      {
        iXPos = iXCount;
        iYPos = iYCount;
        ucMap[iYCount][iXCount] = ' ';
        continue;
      }

      ucMap[iYCount][iXCount] = ucStageMap[uiStage][iYCount][iXCount];
            
    }
  }

  return;

}

LoadMap함수는 미리 지정해놓은 맵에서 히어로의 시작위치를 찾아주고 해당 판 수에 따른 맵을 변수에 저장하기 위함이다.


히어로 움직이는 영역 제한하기

히어로는 길과 점위만 이동할 수 있고 벽은 이동할 수 없다. 그리고 상자는 지나가는 개념이 아니고 다음 방향으로 밀어 줘야한다. 또 이때 상자 다음칸에 벽, 또다른 상자가 있으면 히어로는 이동할 수 없다.

이를 고려해서 코딩을 한다.

case VK_LEFT:
      HbmHero = HbmLeft;
      if (' ' == ucMap[iYPos][iXPos - 1])
      {
        --iXPos;
      }
      else if ('.' == ucMap[iYPos][iXPos - 1])
      {
        --iXPos;
      }
      else if ('B' == ucMap[iYPos][iXPos - 1])
      {
        if (' ' == ucMap[iYPos][iXPos - 2])        // [box] [box]
        {
          ucMap[iYPos][iXPos - 2= ucMap[iYPos][iXPos - 1]; //박스 밀고

          if ('.' != ucStageMap[uiStage][iYPos][iXPos - 1])
          {
            ucMap[iYPos][iXPos - 1= ' ';
          }
          else
          {
            ucMap[iYPos][iXPos - 1= '.';
          }
          --iXPos;
        }
        else if ('.' == ucMap[iYPos][iXPos - 2])
        {
          ucMap[iYPos][iXPos - 2= ucMap[iYPos][iXPos - 1]; //박스 밀고

          if ('.' != ucStageMap[uiStage][iYPos][iXPos - 1])
          {
            ucMap[iYPos][iXPos - 1= ' ';
          }
          else
          {
            ucMap[iYPos][iXPos - 1= '.';
          }
          --iXPos;
        }
      }

      break;

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

    case VK_RIGHT:

      HbmHero = HbmRight;
      
      if (' ' == ucMap[iYPos][iXPos+1])
      {
        ++iXPos;
        stArea.right = (iXPos + 1) * XTILE;
      }
      else if ('.' == ucMap[iYPos][iXPos + 1])
      {
        ++iXPos;
        stArea.right = (iXPos + 1) * XTILE;
      }
      else if ('B' == ucMap[iYPos][iXPos + 1])
      {        
        if (' ' == ucMap[iYPos][iXPos + 2])        // [box]
        {
          ucMap[iYPos][iXPos + 2= ucMap[iYPos][iXPos + 1]; //박스 밀고

          if ('.' != ucStageMap[uiStage][iYPos][iXPos+1])
          {
            ucMap[iYPos][iXPos + 1= ' ';
          }
          else
          {
            ucMap[iYPos][iXPos + 1= '.';
          }          
          ++iXPos;
          stArea.right = (iXPos + 2) * XTILE;

        }
        else if ('.' == ucMap[iYPos][iXPos + 2])
        {
          ucMap[iYPos][iXPos + 2= ucMap[iYPos][iXPos + 1]; //박스 밀고

          if ('.' != ucStageMap[uiStage][iYPos][iXPos + 1])
          {
            ucMap[iYPos][iXPos + 1= ' ';
          }
          else
          {
            ucMap[iYPos][iXPos + 1= '.';
          }
          ++iXPos;          
        }
      }          
      break;

    case VK_UP:      
      HbmHero = HbmFront;

      if (' ' == ucMap[iYPos - 1][iXPos])
      {
        --iYPos;
      }
      else if ('.' == ucMap[iYPos - 1][iXPos])
      {
        --iYPos;
      }
      else if ('B' == ucMap[iYPos - 1][iXPos])
      {
        if (' ' == ucMap[iYPos - 2][iXPos])        // [box] 
        {
          ucMap[iYPos - 2][iXPos] = ucMap[iYPos - 1][iXPos]; //박스 밀고

          if ('.' != ucStageMap[uiStage][iYPos - 1][iXPos])
          {
            ucMap[iYPos - 1][iXPos] = ' ';
          }
          else
          {
            ucMap[iYPos - 1][iXPos] = '.';
          }

          --iYPos;
        }
        else if ('.' == ucMap[iYPos - 2][iXPos])
        {
          ucMap[iYPos - 2][iXPos] = ucMap[iYPos - 1][iXPos]; //박스 밀고

          if ('.' != ucStageMap[uiStage][iYPos - 1][iXPos])
          {
            ucMap[iYPos - 1][iXPos] = ' ';
          }
          else
          {
            ucMap[iYPos - 1][iXPos] = '.';
          }
          --iYPos;
        }
      }
      break;

    case VK_DOWN:    
      HbmHero = HbmBack;

      if (' ' == ucMap[iYPos + 1][iXPos])
      {
        ++iYPos;
      }
      else if ('.' == ucMap[iYPos + 1][iXPos])
      {
        ++iYPos;
      }
      else if ('B' == ucMap[iYPos + 1][iXPos])
      {
        if (' ' == ucMap[iYPos + 2][iXPos])        // [box] [box]
        {
          ucMap[iYPos + 2][iXPos] = ucMap[iYPos + 1][iXPos]; //박스 밀고

          if ('.' != ucStageMap[uiStage][iYPos + 1][iXPos])
          {
            ucMap[iYPos + 1][iXPos] = ' ';
          }
          else
          {
            ucMap[iYPos + 1][iXPos] = '.';
          }

          ++iYPos;
        }
        else if ('.' == ucMap[iYPos + 2][iXPos])
        {
          ucMap[iYPos + 2][iXPos] = ucMap[iYPos + 1][iXPos]; //박스 밀고

          if ('.' != ucStageMap[uiStage][iYPos + 1][iXPos])
          {
            ucMap[iYPos + 1][iXPos] = ' ';
          }
          else
          {
            ucMap[iYPos + 1][iXPos] = '.';
          }
          ++iYPos;
        }
      }

      break


리게임 시키기

해당판을 진행하다 다시 이번 게임을 진행하고 싶을때의 처리를 해준다. Home키를 눌렀을 때 다시 게임을 진행할 수 있도록 만들어 준다.

 case VK_HOME:

      iRet = MessageBox(hWnd, "RE??""RE??", MB_YESNO);
      if (IDYES == iRet)
      {
        InvalidateRect(hWnd, NULL, TRUE);
        LoadMap();
      }
      return 0;


게임의 클리어 처리하기

게임의 클리어는 맵 안의 모든점 위에 박스가 올라가면 클리어라고 정의한다. 이를 판별하기 위해서 현재 맵의 상자의 위치가 기존 맵의 점과 같으면 된다.(점의 위치는 항상 변하지 않는다.) 그러므로 새로 변수를 만들어 맵을 그릴 때 점의 갯수를 카운트 하고, 게임 이동이동마다 항상 박스와 점이 겹치는 수를 카운트해서 둘이 일치하면 게임 클리어가 된다.

그리고 모든 판을 클리어 하였을 때 모든 판을 클리어 하였다고 메세지를 띄운 뒤 게임을 종료하도록 만들어야 한다.

ON_KEYDOWN 함수 마지막에서....

uiDotCount = 0;

  for (iYCount = 0; iYCount < YFRAME; iYCount++)
  {
    for (iXCount = 0; iXCount < XFRAME; iXCount++)
    {
      if ('B' == ucMap[iYCount][iXCount])
      {
        if ('.' == ucStageMap[uiStage][iYCount][iXCount])
        {
          ++uiDotCount;
        }
      }

    }
  }

  if (uiDotCount == uiDotNum)
  {
    MessageBox(hWnd, "Clear""Clear", MB_OK);
    uiStage++;

    if (STAGE <= uiStage)
    {
      MessageBox(hWnd, "Mission All Clear""Mission All Clear", MB_OK);
      SendMessage(hWnd, WM_DESTROY, 00);
      return 0;
    }

    InvalidateRect(hWnd, NULL, TRUE);
    LoadMap();      

  } 


히어로 이동에 따른 무효화 영역 설정

히어로가 이동함에 따라서 모든 맵을 다시 그린다면 엄청난 자원의 낭비를 초래하게 된다. 히어로의 움직임에 따라서 특정영역만 다시 그리게 한다면 많은 이익을 얻을 수 있다. RECT구조체를 사용하여 특정 영역만 무효화할 수 있도록 해보자.

영역 설정 초기화

RECT stArea;  //left, top, Right, bottom

  stArea.left = iXPos * XTILE;
  stArea.top = iYPos * YTILE;
  stArea.right = (iXPos + 1) * XTILE;

  stArea.bottom = (iYPos + 1) *YTILE; 


이동에 따른 무효화 영역 변경

LEFT의 경우 히어로 자신의 현재 자리와 그 오른쪽영역만 다시 그리면 된다. 그러므로 아래와 같은 처리를 해주고, 박스가 존재할 경우 박스도 같이 이동을 시켜 주어야 하므로 이 또한 처리를 해준다. 이와 같은 방법으로 4방향을 모두 처리하면 되겠다.

case VK_LEFT:
      uiScore++;
      HbmHero = HbmLeft;
      if (' ' == ucMap[iYPos][iXPos - 1])
      {
        --iXPos;
        stArea.left = iXPos * XTILE;
      }
      else if ('.' == ucMap[iYPos][iXPos - 1])
      {
        --iXPos;
        stArea.left = iXPos * XTILE;
      }
      else if ('B' == ucMap[iYPos][iXPos - 1])
      {
        if (' ' == ucMap[iYPos][iXPos - 2])        // [box] [box]
        {
          ucMap[iYPos][iXPos - 2= ucMap[iYPos][iXPos - 1]; //박스 밀고

          if ('.' != ucStageMap[uiStage][iYPos][iXPos - 1])
          {
            ucMap[iYPos][iXPos - 1= ' ';
          }
          else
          {
            ucMap[iYPos][iXPos - 1= '.';
          }
          --iXPos;
          stArea.left = (iXPos - 1) * XTILE;
        }
        else if ('.' == ucMap[iYPos][iXPos - 2])
        {
          ucMap[iYPos][iXPos - 2= ucMap[iYPos][iXPos - 1]; //박스 밀고

          if ('.' != ucStageMap[uiStage][iYPos][iXPos - 1])
          {
            ucMap[iYPos][iXPos - 1= ' ';
          }
          else
          {
            ucMap[iYPos][iXPos - 1= '.';
          }
          --iXPos;
          stArea.left = (iXPos - 1) * XTILE;
        }
      }

      break


스코어 처리

푸시푸시는 얼마나 적은 움직임으로 게임을 클리어 하는 것이 관건인 게임이다. 그 움직임을 카운트 하여 더욱 재미를 사용자로 하여금 재미를 유발하여 보자.

방법은 간단하다. 이동키 마다 미리 선언된 변수를 1씩 증가만 시켜 주면 된다.

 case VK_RIGHT:

      .....
      ++uiScore;

case VK_LEFT:
      .....
      uiScore++;

case VK_UP:
      .....
      uiScore++;

case VK_DOWN:
      .....
      uiScore++;

//윈도우 창에 표시(ON_PAINT 에서...)

wsprintf(cTitle, "Stage : %d, Key Count : %d", uiStage + 1, uiScore);
SetWindowText(hWnd, cTitle);

결과



내일 할 일

다듬기( 코드 최적화, UI 새로 스무스하게 그리기)

728x90