-
240201 BSP 알고리즘을 이용한 랜덤맵 생성기록 2024. 2. 2. 15:56
전에 벽으로 나누어진 랜덤맵을 만든 내용을 바탕으로 이를 확장하였다.
240104 BSP Algorithm을 이용한 Random Map Generator
BSP 알고리즘 아이디어 1. 전체공간을 가지고있는 RootNode를 가지고 시작한다. 2. 가로 세로 중 긴 축을 기준으로 랜덤 비율을 사용하여 자른다. 3. 왼쪽 영역을 LeftChildNode, 오른쪽 영역을 RightChildNode
hayo-su.tistory.com
목차
1. Leaf Node 저장
2. Leaf Node 랜덤 룸 생성
3. 룸을 연결하는 통로 생성
4. 결과
1. Leaf Node 저장
말단 노드를 확인하였을 때 LeafNodeList에 저장하도록 하였다.
// 현재 Node를 n개로 나누고싶다는 의미 bool URectRoomMapGenerator::DivideNode(std::shared_ptr<Node> tree, int n, int _size, float _rate) { if (n == 1) // 더이상 방을 나눌 필요가 없을 때 { if (_size > GetRoomSize(tree->nodeRect)) // 최소 방 크기를 만족하지 않는다면 { return false; } LeafNodeList.push_back(tree); return true; }
인덱스, x축 시작점, y축 시작점, x축 끝점, y축 끝점 순으로 leaf노드(말단노드)에 해당하는 노드드를 저장하도록 하였다.
2. Leaf Node 랜덤 룸 생성
for (std::shared_ptr<Node> _leaf : LeafNodeList) { CreateRoom(_leaf); }
모든 말단 노드에 만들 수 있는 도형을 랜덤으로 create하도록 하였다.
삼각형
// 삼각형 if (CurRect.width >= 5 && CurRect.height >= 5) { int point_len = GameEngineRandom::MainRandom.RandomInt(0, CurRect.width - 2); int b_x = CurRect.x; int b_y = CurRect.y + point_len; int a_x = CurRect.x + CurRect.height - 2; int a_y = CurRect.y; int c_x = CurRect.x + CurRect.height - 2; int c_y = CurRect.y + CurRect.width - 2; if (base_map[b_x][b_y] != 0 || base_map[a_x][a_y] != 0 || base_map[c_x][c_y] != 0) { return; } for (int i = CurRect.x; i < CurRect.x + CurRect.height; ++i) { for (int j = CurRect.y; j < CurRect.y + CurRect.width; ++j) { if ((i * (b_y - a_y) < ((b_x - a_x) * (j - b_y) + b_x * (b_y - a_y))) || (i * (b_y - c_y) > ((b_x - c_x) * (j - b_y) + b_x * (b_y - c_y)))) { if (base_map[i][j] == 0) { base_map[i][j] = 1; } } } } }
사각형
// 사각형 if (CurRect.width >= 5 && CurRect.height >= 5) { int start_x = GameEngineRandom::MainRandom.RandomInt(CurRect.x, CurRect.x + CurRect.height / 4); int start_y = GameEngineRandom::MainRandom.RandomInt(CurRect.y, CurRect.y + CurRect.width / 4); int len_x = GameEngineRandom::MainRandom.RandomInt(CurRect.height / 2, CurRect.x + CurRect.height- start_x); int len_y = GameEngineRandom::MainRandom.RandomInt(CurRect.width / 2, CurRect.y + CurRect.width - start_y); for (int i = CurRect.x; i < CurRect.x + CurRect.height; ++i) { for (int j = CurRect.y; j < CurRect.y + CurRect.width; ++j) { if (i < start_x || start_x >= start_x + len_x || j < start_y || j >= start_y + len_y) { if (base_map[i][j] == 0) { base_map[i][j] = 1; } } } } }
마름모
// 마름모 if (CurRect.width >= 3 && CurRect.height >= 3) { int point_x = (CurRect.height) / 2 - 1; int point_y = (CurRect.width) / 2 - 1; int a_x = CurRect.x + point_x; int a_y = CurRect.y; int b_x = CurRect.x; int b_y = CurRect.y + point_y; int c_x = CurRect.x + point_x; int c_y = CurRect.y + point_y * 2; int d_x = CurRect.x + point_x * 2; int d_y = CurRect.y + point_y; for (int i = CurRect.x; i < CurRect.x + CurRect.height; ++i) { for (int j = CurRect.y; j < CurRect.y + CurRect.width; ++j) { if ( (i * (b_y - a_y) < ((b_x - a_x) * (j - b_y) + b_x * (b_y - a_y))) || (i * (b_y - c_y) > ((b_x - c_x) * (j - b_y) + b_x * (b_y - c_y))) || (i * (d_y - a_y) > ((d_x - a_x) * (j - d_y) + d_x * (d_y - a_y))) || (i * (d_y - c_y) < ((d_x - c_x) * (j - d_y) + d_x * (d_y - c_y))) ) { if (base_map[i][j] == 0) { base_map[i][j] = 1; } } } } }
3. 룸을 연결하는 통로 생성
이 때 leaf 노드가 항상 인접한 노드가 아니기 때문에 divide하는 순서를 먼저 바꾸어 줬다.
before
return DivideNode(tree->leftNode, leftnodecnt, _size, nrate) && DivideNode(tree->rightNode, rightnodecnt, _size, nrate);
after
if (true == is_height) { return DivideNode(tree->leftNode, leftnodecnt, _size, nrate) && DivideNode(tree->rightNode, rightnodecnt, _size, nrate); //왼쪽, 오른쪽 자식 노드들도 나눠준다. } else { return DivideNode(tree->rightNode, rightnodecnt, _size, nrate) && DivideNode(tree->leftNode, leftnodecnt, _size, nrate); //왼쪽, 오른쪽 자식 노드들도 나눠준다. }
두 노드를 연결한다.
bool URectRoomMapGenerator::MakeRoad(const RectInt f_rect, const RectInt s_rect) { // x축(세로)가 공통인지 확인 if ((f_rect.x <= s_rect.x && s_rect.x < f_rect.x + f_rect.height) || (f_rect.x >= s_rect.x && s_rect.x + s_rect.height > f_rect.x)) { int midx = (f_rect.GetMidx() + s_rect.GetMidx() - 1) / 2; int miny, maxy; if (s_rect.GetMidy() > f_rect.GetMidy()) { miny = f_rect.GetMidy()-1; maxy = s_rect.GetMidy() + 1; } else { maxy = f_rect.GetMidy()+1; miny = s_rect.GetMidy()-1; } for (int j = miny; j < maxy; ++j) { if (base_map[midx][j] == 1) { if (base_map[midx][j - 1] == 0 || base_map[midx][j + 1] == 0) { base_map[midx][j] = 2; } else { base_map[midx][j] = 3; } } } return true; } // y 축(가로)가 공통인지 확인 else if ((f_rect.y <= s_rect.y && s_rect.y < f_rect.y + f_rect.width) || (f_rect.y > s_rect.y && s_rect.y + s_rect.width > f_rect.y)) { int midy = (f_rect.GetMidy() + s_rect.GetMidy() - 1) / 2; int minx, maxx; if (s_rect.GetMidx() > f_rect.GetMidx()) { minx = f_rect.GetMidx() - 1; maxx = s_rect.GetMidx() + 1; } else { maxx = f_rect.GetMidx() + 1; minx = s_rect.GetMidx() - 1; } for (int j = minx; j < maxx; ++j) { if (base_map[j][midy] == 1) { if (base_map[j - 1][midy] == 0 || base_map[j + 1][midy] == 0) { base_map[j][midy] = 2; } else { base_map[j][midy] = 3; } } } return true; } else { int a = 0; return false; // err } }
4. 결과
통로 연결이 문제가 있어 노드를 재 탐색할지 고민중이다.
'기록' 카테고리의 다른 글
240320 언리얼엔진5 트리거박스 C++코딩 (0) 2024.03.20 240203 BSP 랜덤맵 통로 생성 (0) 2024.02.03 240113 [IOCP] thread 작동 방식 이해하기 (0) 2024.01.13 240112 C++ 백준 런타임에러(NZEC) (1) 2024.01.12 240109 클래스 소멸자 가상화 (0) 2024.01.09