개요
확실히 구조 짜는게 재밌긴 하네요.
처음부터 하나하나 만들어 간다는게 만족감도 크고 재밌어요.
맵 파일 읽기
일단 맵을 렌더링할 InGameScene을 하나 추가해줍니다.
//InGameScene.h
class InGameScene : public Scene
{
public:
InGameScene();
~InGameScene();
public:
void init() override;
void update() override;
void render() override;
};
그리고 맵을 가져와야 하는데 코드에서 수동으로 한줄 한줄 넣어주는건 조금 그러니까 짧게나마 배웠던 파일 입출력을 활용하겠습니다.
맵은 한번만 가져오면 충분하니 init함수 내에서 읽어오겠습니다.
제 팀원이 보내준 맵 텍스트 파일을 소스코드 파일 안에 Map 폴더 안에 넣어준 뒤 fstream이란걸 사용해 가져온 뒤 습관처럼 닫아줍니다.
//InGameScene.cpp
void InGameScene::init()
{
std::fstream mapRead("Map\\Map1.txt");
mapRead.close();
}
그리고 먼저 맵의 너비를 define을 통해 가독성을 좋게 해준뒤에
//Define.h
#define MAP_WIDTH 31
#define MAP_HEIGHT 20
파일을 한글자씩 읽어 줍시다.
한줄씩 읽어줄 수도 있는데 저는 맵 한칸마다 클래스가 하나씩 들어갈 예정이기 한글자씩 읽는게 낫습니다.
//InGameScene.cpp::init()
if (mapRead.is_open())
{
for (int i = 0; i < MAP_HEIGHT - 1; i++)
{
for (int j = 0; j < MAP_WIDTH; j++)
{
char read = mapRead.get();
if (mapRead.fail())
{
cout << "File read error.";
break;
}
}
}
}
읽는데에 실패 했을때의 예외처리도 깔끔하게 해줍니다.
Cell
이제 각각의 칸을 담당할 Cell 클래스를 만들어줍시다.
렌더링 할때 참고해야 하기 때문에 이 클래스는 배경색, 글자색, 적어줄 글자가 필요합니다.
그리고 적이 길을 따라 움직일때 벽을 뚫는 대참사가 나면 안되니 먼저 각각 칸의 타입을 정의해줍시다.
//Type.h
enum class MAP_TYPE
{
WALL,
ROAD,
PLACE
};
각각 벽, 길, 아군 설치 가능 칸 입니다.
정의가 끝났으니 이제 Cell 클래스를 적어줍시다.
//Cell.h
class Cell
{
public:
Cell();
Cell(COLOR col, string str, MAP_TYPE type) : bgColor{ col }, renderString{ str }, _type{ type } {};
public:
MAP_TYPE _type;
COLOR bgColor = COLOR::BLACK;
COLOR charColor = COLOR::WHITE;
string renderString = " ";
};
생성할때 바로바로 정해주기 위해 생성자에 다 때려박았습니다.
이제 MapManager를 적을건데... 참 적을거 많죠? 하지만 객체지향이 원래 이렇죠 뭐... 절차지향으로 하면 다 매개변수로 보내줘야 해서 나중에 더 불편해져요.
//MapManager.h
class MapManager
{
DECLARE_SINGLETON(MapManager)
public:
void setCell(const Cell& cell, int x, int y);
Cell* getCell(int x, int y);
private:
Cell _arrMap[MAP_HEIGHT][MAP_WIDTH];
};
//MapManager.cpp
void MapManager::setCell(const Cell& cell, int x, int y)
{
_arrMap[y][x] = cell;
}
Cell* MapManager::getCell(int x, int y)
{
return &_arrMap[y][x];
}
간단합니다. 그냥 싱글톤으로 만들어주고 x, y값으로 Cell을 설정 및 불러오기 할 수 있게 만들었습니다.
네. 이게 끝이에요.
다시 InGameScene으로 갑시다.
아까 한글자씩 읽어오는 2중 for문 안에서 MapManager에 접근해서 읽어온 글자에 따라 setCell을 해주면 됩니다.
가독성을 올리기 위해선 enum을 써야 하겠지만 일단 귀찮으니까...
//InGameScene.cpp::init() 안의 2중 for문 안.
char read = mapRead.get();
if (read == '0')
{
GET_SINGLETON(MapManager)->setCell(Cell{ COLOR::GRAY , " " , MAP_TYPE::WALL}, j, i);
}
else if (read == '1')
{
GET_SINGLETON(MapManager)->setCell(Cell{ COLOR::LIGHT_GRAY, " ", MAP_TYPE::ROAD }, j, i);
}
else if (read == '2')
{
GET_SINGLETON(MapManager)->setCell(Cell{ COLOR::BLUE, " ", MAP_TYPE::PLACE}, j, i);
}
else if (read == '4')
{
GET_SINGLETON(MapManager)->setCell(Cell{ COLOR::YELLOW, " ", MAP_TYPE::WALL}, j, i);
}
if (mapRead.fail())
{
cout << "File read error.";
break;
}
Type에 까먹고 House를 안넣었지만 일단 넘어가죠.
맵 렌더링
이제 MapManager에 모든 맵의 정보가 들어갔으니 그걸 가져와서 그려주기만 하면 끝입니다.
//InGameScene.cpp::render()
gotoxy(30, 6);
for (int i = 0; i < MAP_HEIGHT; i++)
{
for (int j = 0; j < MAP_WIDTH; j++)
{
setColor(
(int)GET_SINGLETON(MapManager)->getCell(j,i)->charColor,
(int)GET_SINGLETON(MapManager)->getCell(j, i)->bgColor);
cout << GET_SINGLETON(MapManager)->getCell(j, i)->renderString;
}
gotoxy(30, 7 + i);
}
setColor((int)COLOR::WHITE, (int)COLOR::BLACK);
쉽죠?
결과물은 이렇습니다.
파란색은 너무 띄니까 지금은 일단 냅두고 나중엔 좀 덜띄게 바꿔주겠습니다.
마치는 말
뭔가 설명 적기가 힘듧니다.
그도 당연한게 이미 이거 다 끝내고 몇개는 조금 보완하기도 했는데 개발 일지에서 차근차근 스텝을 밟기위해 뇌를 퇴행시켜서 다시 적고 있거든요. 그래도 구조 자체는 여전하기 때문에 문제는 없지만 뭔가 설명을 자세하게 적기가 힘듧니다.
이번엔 코드 블록에 어느 파일에 적힌건지 주석을 좀 넣어봤습니다. 보긴 좀 더 괜찮은 것 같습니다.
'개발 > Default Defense' 카테고리의 다른 글
Default Defense | 다섯번째 프로젝트 # 5 : Enemy Movement (1) | 2024.06.21 |
---|---|
Default Defense | 다섯번째 프로젝트 # 4 : Entity (2) | 2024.06.15 |
Default Defense | 다섯번째 프로젝트 # 2 : Transition (0) | 2024.06.14 |
Default Defense | 다섯번째 프로젝트 # 1 : Scene 구조, 싱글톤 (0) | 2024.06.03 |
Default Defense | 다섯번째 프로젝트 # 0 : 기획 (0) | 2024.06.03 |