본문 바로가기
TIL

셀룰러 오토마타로 육각형 지형 만드는 시스템 구현

by vvin39 2025. 7. 7.

내일배움캠프 62일차 TIL

오늘의 목표는 우리 게임 맵을 사각형 타일맵(Tilemap)으로 구성하면서도, 외형적으로는 육각형 셀 7개가 딱 붙어 있는 것처럼 보이게 하는 것이었다. 그리고 각 육각형 셀 안의 지형은 셀룰러 오토마타(Cellular Automata) 방식을 사용해서 자연스럽게 생성되도록 구현하고 싶었다.

전체 구조

나는 이런 방식으로 시스템을 만들었다.
타일맵(Tilemap): Unity의 기본 사각형 기반 Tilemap을 그대로 사용했다.
외형 구조: 중심에 육각형 셀 하나를 두고, 그 주변 6방향으로 육각형 셀을 배치해서 총 7개의 셀이 보이도록 했다.
지형 생성: 각 셀 안의 지형은 'Plains(들판)', 'Forest(숲)', 'Quarry(채석장)' 세 가지로 나눴고, 각각 랜덤 시드를 기반으로 셀룰러 오토마타를 적용해서 만들었다.

주요 구성 클래스들

이번 시스템을 만들면서 몇 가지 핵심 클래스들을 만들었다.

1. HexGridHelper
이 클래스는 육각형 맵의 좌표 계산을 도와주는 역할을 한다.

Flat-Top 육각형 구조를 기준으로 HexToWorldOffset(q, r, width, height) 함수를 만들었다. 이 함수가 육각형 좌표 (q, r)을 실제 유니티 월드 좌표로 변환해준다.
육각형의 6방향 이웃 정보를 제공하는 기능도 넣었다.

// 이 코드가 Flat-Top 육각형 배열의 위치를 정렬해주는 핵심 수식이다.
// q와 r은 육각형 좌표계의 열과 행이다.
float x = q * (maxWidth * 0.75f); // 가로 간격 조절
float y = r * height;             // 세로 위치 조절
if (q > 0) y += height * 0.5f;    // q가 양수면 y를 반 칸 올리고
else if (q < 0) y -= height * 0.5f; // q가 음수면 y를 반 칸 내린다.


이전 TIL에서도 이 Flat-Top 좌표 계산이 중요하다고 느꼈었는데, 이번에도 역시 핵심이었다.

2. HexMapData
이 클래스는 각 육각형 셀 내부의 지형 데이터를 관리한다.

TerrainType[,] terrainMap이라는 2차원 배열로 각 셀 안의 지형 정보를 저장했다.
GenerateCellularTerrain() 함수를 만들어서 초기 지형을 랜덤으로 채운 다음, 여러 차례 스무딩(smoothing) 작업을 거치게 했다.
스무딩할 때는 주변 이웃 셀의 Forest/Plains/Quarry 개수를 세서, 다수를 차지하는 지형으로 수렴하게 만들었다.

// 이 코드가 지역적으로 비슷한 지형이 뭉쳐 나오게 유도하는 부분이다.
// 주변 5개 이상이 Forest면 나도 Forest가 되고, Plains면 Plains가 된다.
// Quarry는 4개 이상만 되어도 Quarry가 되게 해서 좀 더 자주 보이게 했다.
if (forest >= 5) newMap[x, y] = TerrainType.Forest;
else if (plains >= 5) newMap[x, y] = TerrainType.Plains;
else if (quarry >= 4) newMap[x, y] = TerrainType.Quarry;


이렇게 하니까 지형들이 자연스럽게 뭉쳐서 마치 실제 지형처럼 보였다.

3. HexMapGenerator
이 클래스는 HexMapData에서 받은 지형 데이터를 실제 타일맵에 그리는 역할을 한다.

셀 하나의 지형 데이터를 받아서 유니티의 타일맵에 실제 타일을 배치한다.
TerrainType에 따라 적절한 TileBase를 선택해서 그렸다.


4. HexMapManager
이 클래스는 전체 시스템을 총괄하는 매니저 역할을 한다.

7개의 육각형 셀(중심 + 육방향)의 위치를 정의했다.
각 위치마다 HexMapData를 새로 생성하고, 셀룰러 오토마타를 적용해서 지형을 채우게 했다.
그 다음 HexMapGenerator를 호출해서 생성된 지형을 타일맵에 그리도록 명령했다.


결과

각 셀 내부는 사각형 타일로 구성되어 있지만, 전체적으로는 내가 의도했던 육각형처럼 보이는 외형을 가졌다.
각 셀 안에서 Forest, Plains, Quarry 지형이 적절히 섞여서 자연스러운 모습을 보여줬다.
하나의 타일맵 안에 다양한 지형이 살아 있는, 자연스러우면서도 반복적이지 않은 맵 연출이 가능해졌다.


📊 Perlin Noise와 Cellular Automata를 비교해봤다

이번에 지형을 만들면서 Perlin Noise와 Cellular Automata 두 가지 방식을 고민했다.

항목  Perlin Noise Cellular Automata
원리  좌표 기반의 연속적인 잡음 함수 이웃 기반의 상태 전이 반복
패턴  부드럽고 연속적인 형태 뭉침, 불균형도 표현 가능
랜덤성   시드 기반으로 연속적인 값을 생성 초기 랜덤값 + 이웃 기반으로 진화
제어성 Noise Scale, Threshold로 조절 Iteration 수, 임계치로 조절
적합한 용도 높낮이, 지형 등급 구분 던전, 바이옴, 구조적 변화 표현


이번 구현에서는 지형 간의 뚜렷한 경계와 지역적으로 뭉치는 특성을 표현하는 것이 중요했기 때문에, 셀룰러 오토마타가 훨씬 적합하다고 판단했다. Perlin Noise는 너무 부드러워서 내가 원하는 '구역' 느낌을 내기 어려웠다.


🤔 내가 느낀 점

육각형처럼 보이게 하기 위해 Flat-Top 기준 좌표 계산이 정말 중요한 포인트였다. 이 부분을 잘 잡으니까 나머지가 술술 풀렸다.
셀룰러 오토마타의 이웃 기반 스무딩 방식이 지역적인 특징을 표현하는 데 매우 유용하다는 것을 직접 경험했다. 정말 자연스럽게 뭉치는 모습을 보면서 신기했다. 
사각형 타일맵을 사용하더라도, 배치 방식과 오프셋을 잘 조절하면 육각형처럼 보이게 할 수 있다는 것을 다시 한번 확인했다. 앞으로 다른 형태의 맵을 만들 때도 이 원리를 응용할 수 있을 것 같다.


💡 내가 꼭 기억할 포인트들

Flat-Top 육각형 배치는 x * 0.75f와 y + (q > 0 ? +0.5f : -0.5f) 방식으로 y축을 보정해야 한다. 셀룰러 오토마타의 핵심은 초기 랜덤값보다 반복적인 이웃 기반 스무딩에 있다. 사각형 타일맵으로도 배치 방식과 오프셋을 잘 조절하면 육각형처럼 보이게 할 수 있다!

 

 

 

최근댓글

최근글

skin by © 2024 ttuttak