덧글수2
이번에는 지난 NDC2016 발표의 "맵 생성 절차"(59~69p.)에 대해서 좀 더 자세한 글을 써보려고 한다. 59페이지를 그대로 가져와보면 다음과 같다.

이 단계들에 대해 차례대로 설명하려고 한다.
1. Poisson Disc Sampling
맵에서 비교적 균일한 간격으로 노드를 선택하기 위한 방법이다. 완전한 랜덤이 아닌 이 방법으로 선택한 노드는 보기에도 좋고 활용도 편리하다. 하나하나의 노드가 도시나 별이라고 하면 서로 너무 멀거나 너무 가깝지 않은 적당한 거리를 유지할 수 있기 때문에 자연스럽기도 하다. 아래 그림에서 가장 오른쪽이 Poisson Disc Sampling 으로 생성한 노드들이다.

Disc Sampling 이라는 이름대로 이 방법은 대기열에 속한 점(=노드) 주변에 있는 원형(Disc)의 영역에 다른 점을 추가할 수 있는지 검사한다.

새로 추가하는 점 주변에 다른 이웃이 없다면 그 점을 추가하고, 그 점을 대기열에 넣는다.
미리 정해진 tryCount 만큼 탐색하여 점을 여러 개 추가한다. 나는 tryCount를 30으로 설정했다.

tryCount 가 끝나면 대기열에 있던 점을 빼고 새로 추가된 점 중 하나를 대기열에 집어넣고 다음 탐색을 반복한다. 이때 주변에 다른 이웃이 없는지 체크해서 이웃이 있다면 그 점은 추가될 수 없다.

탐색을 빠르게 하기 위해 전체 맵을 그리드로 나누고, 그리드 안에는 하나의 점만 들어갈 수 있도록 제한한다. 따라서 원이 겹치는 영역에 있는 모든 점을 검사할 필요 없이, 원이 걸치는 그리드에 있는 점들만 검사하면 되기 때문에 탐색 시간이 줄어든다.

이해를 돕기 위해 예전에 만들었던 Poisson Disc Sampling 예제 링크를 올린다. 예제에서는 1프레임당 1개의 점을 탐색하도록 했기 때문에 점이 천천히 검색되는 것이지, 실제 검색 속도는 빠르다.

2. Minimal Spanning Tree
트리란 두 개의 노드가 하나의 간선(edge)으로만 연결된 그래프를 말한다.

신장 트리(Spanning Tree)는 사이클(루프)가 없이 모든 노드를 연결하는 트리이다. 최소 신장 트리(Minimal Spanning Tree)는 이 중 모든 노드를 연결하면서 경로의 합이 가장 짧은 신장 트리를 말한다.
위에서 만든 노드들을 연결해서 그래프를 만드는데, 복잡한 계산을 줄이기 위해 일정 길이 이하의 간선만 남긴다. 그리고 여기서 최소 신장 트리를 찾는다.

3. 지형 배치
이렇게 최소 신장 트리를 찾은 다음에는 간선 연결이 하나뿐인 터미널 노드(terminal node)를 찾는데, 이곳에 물과 절벽을 배치한다. 터미널 노드에는 물이나 절벽 중 하나를 배치한다.
아래 그림에서 검은색으로 표시된 노드가 터미널 노드이다. 두번째 글에서 설명했던 Cellular Automata 기법으로 인접한 물, 인접한 절벽끼리는 서로 합쳐진 것을 확인할 수 있다.

이제 각 타일이 어느 지형에 속하는지 결정을 해야 한다. 어떤 타일은 풀이 먼저 그려진 다음 위에 물이 그려지고, 또 절벽까지 겹쳐져 있다. 일단 절벽(검은색)을 최우선으로 찾은 다음 물(파란색)을 찾고, 마지막으로 절벽과 물에 속하지 않은 곳을 이동 가능한 타일(하얀색)로 지정한다.

또한 경로에는 랜덤하게 길 타일을 배치해서 사실감을 높인다. 길 타일 역시 Cellular Automata 기법으로 너무 뚜렷하지 않게 자연스러운 모양을 유지하도록 했다.
4. 다시 Poisson Disc Sampling - 메인 경로 추출
지금까지의 작업이 그럴듯해 보이는 지형을 만들기 위한 것이었다면 이번 단계는 유닛들이 이동할 수 있는 메인 경로를 만드는 작업이다. 다시 Poisson Disc Sampling 을 이동 가능한 타일(하얀색)에 대해서만 수행한다. 그리고 경로는 이동 가능한 타일을 지나는 것만 인정하고, 중간에 절벽이나 물과 겹치는 경로는 제외한다.


5. 메인 노드 정하기 & 유닛 배치
그럼 이제 1:1 대결을 상정한 맵이기 때문에 각 팀의 유닛을 배치할 메인 노드를 결정한다. 메인 노드 2개는 서로 연결되어야 하고, 그 거리는 맵의 가로 길이 + 맵의 세로 길이의 40% 에서 100% 사이의 값이 되어야 한다. 적당한 메인 노드 2개를 발견하지 못했을 경우 맵 전체를 다시 생성한다.
메인 노드에는 각 팀이 지켜야 할 타워를 생성하고 그 주위에 유닛을 배치한다. 이제 양 팀은 서로의 타워를 파괴하기 위해 서로 대결을 펼치게 된다. 참고로 타워가 구석에 있을수록 유리한데, 보병과 기병은 인접해야만 타워에 공격이 가능하기 때문이다. 이 맵에서 Blue Team의 승률은 300번의 전투 시뮬레이션을 돌렸을 때 85.0%가 나왔다.

약간 길었지만 발표 중에 가장 정리하고 싶었던 부분인 맵 생성 절차를 정리해 보았다. 다음에는 시간이 된다면 딥러닝 실행 부분을 정리해보려고 한다. 양이 많아서 아마 글 하나로는 안될 것 같다.

이 단계들에 대해 차례대로 설명하려고 한다.
1. Poisson Disc Sampling
맵에서 비교적 균일한 간격으로 노드를 선택하기 위한 방법이다. 완전한 랜덤이 아닌 이 방법으로 선택한 노드는 보기에도 좋고 활용도 편리하다. 하나하나의 노드가 도시나 별이라고 하면 서로 너무 멀거나 너무 가깝지 않은 적당한 거리를 유지할 수 있기 때문에 자연스럽기도 하다. 아래 그림에서 가장 오른쪽이 Poisson Disc Sampling 으로 생성한 노드들이다.

Disc Sampling 이라는 이름대로 이 방법은 대기열에 속한 점(=노드) 주변에 있는 원형(Disc)의 영역에 다른 점을 추가할 수 있는지 검사한다.

새로 추가하는 점 주변에 다른 이웃이 없다면 그 점을 추가하고, 그 점을 대기열에 넣는다.
미리 정해진 tryCount 만큼 탐색하여 점을 여러 개 추가한다. 나는 tryCount를 30으로 설정했다.

tryCount 가 끝나면 대기열에 있던 점을 빼고 새로 추가된 점 중 하나를 대기열에 집어넣고 다음 탐색을 반복한다. 이때 주변에 다른 이웃이 없는지 체크해서 이웃이 있다면 그 점은 추가될 수 없다.

탐색을 빠르게 하기 위해 전체 맵을 그리드로 나누고, 그리드 안에는 하나의 점만 들어갈 수 있도록 제한한다. 따라서 원이 겹치는 영역에 있는 모든 점을 검사할 필요 없이, 원이 걸치는 그리드에 있는 점들만 검사하면 되기 때문에 탐색 시간이 줄어든다.

이해를 돕기 위해 예전에 만들었던 Poisson Disc Sampling 예제 링크를 올린다. 예제에서는 1프레임당 1개의 점을 탐색하도록 했기 때문에 점이 천천히 검색되는 것이지, 실제 검색 속도는 빠르다.

2. Minimal Spanning Tree
트리란 두 개의 노드가 하나의 간선(edge)으로만 연결된 그래프를 말한다.

신장 트리(Spanning Tree)는 사이클(루프)가 없이 모든 노드를 연결하는 트리이다. 최소 신장 트리(Minimal Spanning Tree)는 이 중 모든 노드를 연결하면서 경로의 합이 가장 짧은 신장 트리를 말한다.
위에서 만든 노드들을 연결해서 그래프를 만드는데, 복잡한 계산을 줄이기 위해 일정 길이 이하의 간선만 남긴다. 그리고 여기서 최소 신장 트리를 찾는다.

3. 지형 배치
이렇게 최소 신장 트리를 찾은 다음에는 간선 연결이 하나뿐인 터미널 노드(terminal node)를 찾는데, 이곳에 물과 절벽을 배치한다. 터미널 노드에는 물이나 절벽 중 하나를 배치한다.
아래 그림에서 검은색으로 표시된 노드가 터미널 노드이다. 두번째 글에서 설명했던 Cellular Automata 기법으로 인접한 물, 인접한 절벽끼리는 서로 합쳐진 것을 확인할 수 있다.

이제 각 타일이 어느 지형에 속하는지 결정을 해야 한다. 어떤 타일은 풀이 먼저 그려진 다음 위에 물이 그려지고, 또 절벽까지 겹쳐져 있다. 일단 절벽(검은색)을 최우선으로 찾은 다음 물(파란색)을 찾고, 마지막으로 절벽과 물에 속하지 않은 곳을 이동 가능한 타일(하얀색)로 지정한다.

또한 경로에는 랜덤하게 길 타일을 배치해서 사실감을 높인다. 길 타일 역시 Cellular Automata 기법으로 너무 뚜렷하지 않게 자연스러운 모양을 유지하도록 했다.
4. 다시 Poisson Disc Sampling - 메인 경로 추출
지금까지의 작업이 그럴듯해 보이는 지형을 만들기 위한 것이었다면 이번 단계는 유닛들이 이동할 수 있는 메인 경로를 만드는 작업이다. 다시 Poisson Disc Sampling 을 이동 가능한 타일(하얀색)에 대해서만 수행한다. 그리고 경로는 이동 가능한 타일을 지나는 것만 인정하고, 중간에 절벽이나 물과 겹치는 경로는 제외한다.


5. 메인 노드 정하기 & 유닛 배치
그럼 이제 1:1 대결을 상정한 맵이기 때문에 각 팀의 유닛을 배치할 메인 노드를 결정한다. 메인 노드 2개는 서로 연결되어야 하고, 그 거리는 맵의 가로 길이 + 맵의 세로 길이의 40% 에서 100% 사이의 값이 되어야 한다. 적당한 메인 노드 2개를 발견하지 못했을 경우 맵 전체를 다시 생성한다.
메인 노드에는 각 팀이 지켜야 할 타워를 생성하고 그 주위에 유닛을 배치한다. 이제 양 팀은 서로의 타워를 파괴하기 위해 서로 대결을 펼치게 된다. 참고로 타워가 구석에 있을수록 유리한데, 보병과 기병은 인접해야만 타워에 공격이 가능하기 때문이다. 이 맵에서 Blue Team의 승률은 300번의 전투 시뮬레이션을 돌렸을 때 85.0%가 나왔다.

약간 길었지만 발표 중에 가장 정리하고 싶었던 부분인 맵 생성 절차를 정리해 보았다. 다음에는 시간이 된다면 딥러닝 실행 부분을 정리해보려고 한다. 양이 많아서 아마 글 하나로는 안될 것 같다.
최근 덧글