티스토리 뷰

Project

#005 : Unlight Copycat [DAY #04]

BaeMinCheon 2018. 2. 25. 22:16

Project Note #005


Unlight Copycat DAY #04

개요

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

환경

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

Quest 시퀀스의 세부내용 구현을 마저 진행합니다. Deck 구조체의 name을 string에서 TCHAR로 변경합니다(아무래도 여러 번 출력될 값인데 매번 변환하는 것보다는, 저장할 때 한 번만 변환하는 것이 효율적이겠죠). 그리고 string에서 TCHAR로 변환하는 코드가 여러 번 사용되므로 함수로 따로 작성했습니다(Utitliy.h). 접근성을 위해 Deck 구조체의 카드번호 변수들을 배열로 변경했습니다.


Quest 시퀀스에서의 카드 출력을 위해 간단한 비트맵 파일을 준비합시다. 포토샵을 사용해 검은 테두리와 정중앙의 BACK 문구를 적은 bmp 파일을 만들었습니다. 이렇게 만든 리소스를 사용하기 위해서, 본 프로젝트(Game)에서 리소스파일(.rc)을 추가하고 리소스 추가로 비트맵을 가져옵니다. 이 과정을 거치면 가져온 비트맵의 ID값이 resource.h에 #define으로 정의됩니다.


pclaf에서의 비트맵 파일 사용법

  • loadBitmap() : 비트맵 리소스의 ID를 매개변수로 받아, BITMAP 객체로 반환
  • drawBitmap() : 비트맵 객체와 출력위치 그리고 비트맵크기를 입력받아 화면에 해당 비트맵 객체를 출력


앞으로 비트맵 리소스를 몇 개나 추가할지는 모르지만, 카드 번호는 가능한 기존 Unlight의 카드 번호를 따라가도록 합시다(에바리스트부터 001 순서). 그리고 000번은 카드 뒷면으로 정하면 되겠네요. 그런데 여기서 아쉬운 상황이 생깁니다. 카드 번호와 리소스 ID를 1:1 대응시킬 수는 없다는 것이죠. 왜냐하면 리소스 ID는 1부터 사용할 수 있기 때문입니다.


따라서 우리는 카드번호는 0번부터 시작하되, 리소스 ID는 1번부터 시작해야합니다. 비트맵 리소스를 부를 때에는 카드번호에 1을 더해 해결할 수 있는 간단한 문제이지만, 딱 맞아떨어지지 않아 아쉽네요. 그리고 Quest 시퀀스에서 방금 제작한 카드번호 0번 리소스 ID 1인 카드뒷면 bmp 파일을 출력하도록 합시다(덱에 카드뒷면을 넣는 건 말이 안 되지만, 지금은 디버깅용으로 잠시 사용하겠습니다).


제작한 bmp 파일의 크기는 가로세로 200*300px입니다(Battle 시퀀스에서 사용하려면 이 정도 크기가 적당하기 때문입니다). Quest 시퀀스의 덱위치에 들어가려면 반으로 축소시켜야겠군요. 하지만 pclaf에서는 비트맵을 변형(resize)하는 함수를 지원하지않습니다. 그래서 저는 float 매개변수를 추가로 받는 drawBitmap()을 pclaf에 정의했습니다. 이 함수는 StretchBlt()을 사용해 비트맵을 늘리거나 줄일 수 있습니다.


그럼 Quest 시퀀스의 draw()에서 새로 만든 drawBitmap()을 사용해봅시다. 기존 drawBitmap()와는 달리 float 변수를 하나 더 받는데, 이 변수는 확대 또는 축소의 비율을 전달해줍니다. 0.5를 매개변수로 전달하니 적당한 크기로 출력이 잘 되는군요. 이렇게 덱 내에서 카드가 있는 자리는 비트맵 리소스를 출력하고, 그렇지 않는 자리는 rectangle()을 사용해 비어있다는 느낌을 줍시다.



그럼 이제 Quest 시퀀스의 나머지를 작성해봅니다. 맵목록과 맵을 클릭하면 맵의 정보가 뜨는 등의 기능이 있겠군요. 오늘은 맵목록을 추가하고 출력하는 작업까지만 진행하겠습니다. 먼저 맵목록을 어떻게 처리할지 고민해봅시다. 맵 또한 유저정보처럼 실행할 때마다 이전 정보가 남아있어야 하고 게임을 종료할 때도 현재 정보를 남길 수 있어야하겠죠. 그럼 역시 파일 입출력이 들어가야겠습니다.


저는 mapList.txt라는 파일을 만들고 해당 파일 내에 맵목록을 작성하겠습니다. 유저정보를 파일로 저장할 때와 비슷한 양식으로 파일을 작성하고, 비슷한 구조로 불러옵니다. 맵에 있어 필수적인 정보는 이름/소요AP/칸의개수/각칸의내용이 있겠습니다. 파일 내에서 이름은 name 뒤에 작성하고, 소요AP는 cost 뒤에, 칸의 개수는 length 뒤에, 각 칸의 내용은 start 뒤에서 각 칸당 4개의 정수로 작성해줍니다.


start 뒤에서 작성하는 각 칸의 내용을 정수 4개로 정한 이유는 다음과 같습니다. 첫번째 정수는 해당 칸에 어떤 물체가 있는지, 두번째 정수부터 네번째 정수까지는 해당 물체에 대한 정보를 담고 있을 겁니다. 비어있는 칸이나 아이템만 있는 칸이라면 정수가 4개나 필요하지않겠지만, 몬스터가 있는 칸이라면 몬스터가 한 번에 최대 3마리까지 등장할 수 있기 때문에 정수 3개를 확보하는 것이 좋습니다.


그리고 mapList.txt는 Quest의 생성자에서 읽도록 작성합니다. 유저정보처럼 자기 자신의 생성자에서 읽으면 간편해서 좋겠지만, 맵은 여러 개로 존재할 수 있기 때문에 더 큰 범주에서 읽도록 합시다. 유저정보와는 다르게 파일 하나에 여러 개의 정보가 들어있기 때문에, 필요한 값들을 저장할 변수들을 정적 변수로 사용합니다(반복문을 돌면서 초기화되는 것을 방지하기 위함입니다).


맵 자체는 스스로 출력할 수 있어야하고, 클릭을 검출하기도 해야하기 때문에 클래스로 작성하며 동시에 RectButton을 상속받는 게 좋겠네요. RectButton을 상속에 활용하기 위해 private 접근자를 protected로 바꿔 씁니다. 부모 클래스(ButtonRectButton)의 모든 멤버를 사용하진 않겠지만, 덕분에 버튼으로서의 역할을 하기 위한 코드를 생략할 수 있게 되었습니다.


그리고 정적 멤버변수로 counter를 두어 각 맵이 맵목록에서 몇 번째인지를 계산할 수 있도록 합시다. draw()를 손보는 것으로 순차적인 출력 자체는 가능하겠지만, 클릭을 검출하려면 결국 멤버변수 leftX leftY width height 들을 제대로 초기화해야합니다. 추후 맵을 삭제하는 기능을 만들면 counter값을 변경하는 코드도 작성해야겠네요.


상속받은 draw()를 맵목록에 맞도록 재정의해줍니다. 그럼 이제 파일 입력을 통해 MapList가 mapVector로 추가되고 화면에 출력되겠군요. 파일을 수정해 또 다른 맵을 추가했더니 잘 작동합니다. 오늘은 Battle 시퀀스 작성에 들어가나 했더니 못했네요ㅠ Quest 시퀀스와 Battle 시퀀스를 작성하는 데에 이렇게나 손이 많이 갈 줄을 몰랐습니다.... 이 다음으로는 Quest 시퀀스를 마저 완성하는 과정을 진행하겠습니다. 이상으로 DAY #04 노트를 마칩니다.



'Project' 카테고리의 다른 글

#007 : Unlight Copycat [DAY #08]  (0) 2018.03.01
#006 : Unlight Copycat [DAY #05]  (0) 2018.02.26
#004 : Unlight Copycat [DAY #03]  (0) 2018.02.25
#003 : Unlight Copycat [DAY #02]  (0) 2018.02.23
#002 : Unlight Copycat [DAY #01]  (0) 2018.02.22
공지사항
최근에 올라온 글
최근에 달린 댓글
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
글 보관함