티스토리 뷰

Project

#009 : Unlight Copycat [DAY #18]

BaeMinCheon 2018. 3. 11. 20:23

Project Note #009


Unlight Copycat DAY #18

개요

  • Window API의 일종인 pclaf를 활용해 게임을 만드는 과정을 정리합니다.
  • 참고
    ※ https://github.com/BaeMinCheon/unlight-copycat (Github, "v0.8.0" 태그)

환경

  • Visual Studio 2015 Professional
  • Windows 10 Home
  • pclaf (C/C++)

맵블럭을 눌렀을 때 시퀀스가 전환되도록 해야하므로, Map::Map()에서 맵블럭 객체를 blockVector에 집어넣는 코드를 수정합니다. 첫번째 블럭을 제외한 나머지 블럭의 make_shared<>()에 대해서 ++GameWindow::sequenceIndex;를 람다표현식으로 전달합니다. 그리고 Quest::leftClick()에서 다음 맵블럭을 클릭했을 때 맵블럭의 doWork()를 호출하는 코드를 작성합니다.


mapInProcess == true일 경우 실행되는 코드들을 수정해야겠습니다. 시퀀스를 전환하는 기능을 추가하면서, 마지막 맵블럭에 도달했을 때 포기버튼이 사라지지않는 버그가 생겼군요. 이를 위해 if-else 구조였던 코드를 if-if 구조로 변경합니다. 이렇게 하면, Map::position이 증가한 뒤에 마지막 맵블럭 위치인지 검사할 수 있게 됩니다. 다시 실행해보니 의도한 대로 잘 작동하네요.


맵블럭을 눌렀을 때 Battle 시퀀스로 넘어가는 것을 확인했으니, 눌러도 Battle 시퀀스로 넘어갈 필요가 없는 맵블럭일 경우에 대한 처리를 합시다. 맵블럭의 종류를 크게 3가지로 나누겠습니다. ① 아무 것도 없는 맵블럭 ② 아이템만 있는 맵블럭 ③ 몬스터 또는 캐릭터가 있는 맵블럭 (mapList.txt에서는 012 순서로 구분하겠음)


이 중에서 ③번만이 Battle 시퀀스로 넘어갈 필요가 있습니다. 따라서, Map::Map()을 다시 수정해줍니다. blockVector.push_back()을 반복하는 for문에서 switch문을 사용해 맵블럭의 종류에 따라 다른 람다표현식을 전달할 수 있도록 작성합니다. case 0일 때 즉, 빈 맵블럭인 경우 아무것도 하지않습니다. case 1일 때 즉, 아이템이 있는 맵블럭인 경우도 우선 아무것도 하지않도록 합니다. 추후에 작성하도록 합시다. case 2일 때에는 이전에 만들었던 시퀀스 전환 코드를 작성해줍니다.


실행해서 확인해보면 mapList.txt에서 맵블럭 정보의 첫번째 정수가 2로 작성한 맵블럭만 Battle 시퀀스로 진입함을 알 수 있습니다. switch문에 default문을 추가해 잘못된 파일인 경우 예외를 던지도록 합시다(throw exception("MAPBLOCK TYPE ERROR");).


아 그러고 보니 시작버튼을 눌렀을 때 AP가 소모되는 기능을 깜빡하고 만들지않았군요. Map 클래스에 getCost()를 정의해주고, 시작버튼의 람다표현식에서 apDelta(-getCost())를 추가해줍니다. 시작버튼을 더블클릭하면 AP가 소모되는 것을 볼 수 있습니다(파일 출력은 아직 구현하지 않아, 감소된 상태가 저장되지는 않음).


Battle 시퀀스에 대한 작업을 본격적으로 시작하기 이전에, 아이템만 있는 맵블럭에 대한 처리를 끝냅시다. 우선 아이템을 획득했다는 안내문구를 띄우는 것부터 해봅니다. 아이템의 bmp 리소스를 포함하는 박스를 만들어야하므로 기존에 만들었던 OKBox를 그대로 사용할 수는 없습니다. RectButton을 상속받는 ItemBox를 선언합시다.


RectButton에서 상속받는 멤버함수 중 draw()와 isClick() 그리고 moveTo()를 손보면 될 것 같네요. draw()의 경우, RectButton::draw()를 먼저 작성하고 뒤에 drawBitmap()을 붙여줍니다. 그리고 아이템 이름과 획득 개수를 wout으로 출력합니다. drawBitmap()에서 리소스 ID값이 필요하기 때문에 ItemBox의 멤버변수로 int id;를 선언해줍니다(지금은 GEM에 대한 비트맵 리소스만 추가하며, 아이템의 ID값은 10000부터 시작함. 즉, 리소스 ID는 10001부터 시작함). 그리고 wout으로 아이템의 이름과 획득개수를 출력하기 위해 변수를 또 선언해줘야겠네요.


아이템 이름은 GameWindow 클래스에 std::map<int, TCHAR*> itemNameMap을 정적 멤버변수로 추가한 뒤, ItemBox 클래스에서 GameWindow::itemNameMap[id]로 사용하도록 합니다. 획득개수의 경우 매번 다른 값을 가질 수 있으니, 그냥 ItemBox 클래스의 멤버변수로 선언해줍니다(int numberOfItem;).


마지막으로 OK버튼도 출력해야겠네요. ItemBox에 멤버변수로 RectButton ok;를 추가하고, draw()에 마지막으로 ok.draw(app);을 작성해줍니다. 여러 번 실행을 해보며 구성요소들이 적절한 위치를 가지도록 합니다. 그 결과물은 아래와 같습니다(아직 맵블럭과의 연동은 진행하지않았음. 임시로 출력한 결과물임). 나머지 isClick()과 moveTo()는 OKBox와 동일한 구조를 가집니다. moveTo()에서 OK버튼의 위치를 결정하는 수치가 약간 다를 뿐입니다.



이왕 map이 쓰인 김에 시퀀스들을 정리해볼까 합니다. Quest 시퀀스가 너무 복잡해져 분할할 필요가 있다는 생각이 드네요. 시퀀스를 담는 자료구조를 vector로 유지한다면, 시퀀스를 빼고 넣을 때마다 해당 vector를 사용하는 코드들을 수정해줘야하기 때문에 매우 비효율적입니다. 따라서 기존에 사용하던 sequenceVector를 sequenceMap으로 바꾸는 작업을 해봅시다.


우선 sequenceVector와 sequenceIndex를 지웁니다. 그리고 std::map<std::string, std::shared_ptr<Sequence>> sequenceMap과 std::string currentSequence로 대체합니다. 그리고 시퀀스를 전환하기 위해 sequenceVector와 sequenceIndex를 사용하던 코드들을 수정합니다(예컨대, ++GameWindow::sequenceIndex; → GameWindow::currentSequence = "Quest;").


이런 일련의 과정으로 시퀀스의 추가 및 삭제가 쉬워졌습니다. 그럼 이제 Quest 시퀀스를 나눠볼까 합니다. Quest 시퀀스에 너무 많은 코드가 몰려있는 것 같아, 최소한 두 개 이상으로 나누어야 추후 유지보수에도 도움이 될 것 같네요. 우선, Quest.h와 Quest.cpp에 있던 Map 관련 내용들을 새로 만들 Map.h와 Map.cpp로 옮깁시다.


그리고 Quest 클래스 내에 있던 mapVector mapListIndex mapInProcess를 Map 클래스로 옮깁니다. 이에 따라 변경해야 하는 코드들을 수정해줍니다. 오늘은 여기까지 작업하도록 하겠습니다. 이 다음으로는 Quest 시퀀스를 세분화하고 코드를 정리하는 과정을 진행하겠습니다.

'Project' 카테고리의 다른 글

#011 : Unlight Copycat [Episode #09]  (0) 2018.08.31
#010 : Speedrun Gunner  (0) 2018.06.22
#008 : Unlight Copycat [DAY #09]  (0) 2018.03.02
#007 : Unlight Copycat [DAY #08]  (0) 2018.03.01
#006 : Unlight Copycat [DAY #05]  (0) 2018.02.26
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함