본문 바로가기
TIL

육각형 맵 Flat-Top 전환 & 6방향 랜덤 지형 배치

by vvin39 2025. 7. 4.

내일배움캠프 61일차 TIL

🎯 오늘 한 일

오늘의 목표는 우리 게임의 육각형 맵 구조를 Pointy-Top 방식에서 Flat-Top 방식으로 완전히 전환하는 것이었다. 그리고 중심 육각형을 기준으로 6방향으로 확장된 육각형 맵을 배치하고, 각 육각형에 지형(들판, 숲, 채석장)을 랜덤으로 적용하는 시스템을 구현했다! 드디어 맵이 좀 더 게임다워진 기분이다!

Pointy-Top → Flat-Top으로 바꾼 이유

기존 문제점

.처음에는 육각형 좌표계(큐브 좌표나 오프셋 좌표)를 이용해서 Even-Q 기준의 Pointy-Top 육각형을 구현했었다. 그런데 막상 만들어보니 좀 아쉬운 점이 많았다.

시각적인 불편함

상하로 뾰족하고 좌우로 넓게 퍼지는 형태라서, 맵 전체를 볼 때 카메라 구성이나 UI와의 일관성이 떨어지는 느낌이었다.

 

연결감 부족

육각형들이 뭔가 자연스럽게 이어지는 느낌이 적었다.

 

그래서 Flat-Top 방식으로 바꾸기로 결정했다. 바꿔보니 훨씬 좋았다!

직관적인 방향성

위아래가 평평한 형태라 위/아래/왼/오른쪽 같은 직관적인 방향성이 맞아떨어졌다.


확장성 및 길 생성

위에서 아래로 확장되는 느낌을 주기 좋았고, 무엇보다 나중에 길을 생성하거나 카메라 움직임을 구현하는 데 훨씬 간단하고 유리할 것이라고 판단했다.

 

맵 구조 및 좌표계는 이렇게 구성했다!

육각형 위치 계산 (Flat-Top 방식)

Flat-Top 방식에 맞춰 육각형 좌표 (q, r)을 기준으로 6방향을 정의했다. 이 방향들을 이용해서 주변 육각형을 찾아낼 수 있다.

public static readonly Vector2Int[] Directions = new Vector2Int[]
{
    new Vector2Int(1, 0),   // 오른쪽 (q 증가)
    new Vector2Int(-1, 0),  // 왼쪽 (q 감소)
    new Vector2Int(0, 1),   // 위 (r 증가)
    new Vector2Int(0, -1),  // 아래 (r 감소)
    new Vector2Int(1, -1),  // 오른쪽 아래 (q 증가, r 감소)
    new Vector2Int(-1, 1)   // 왼쪽 위 (q 감소, r 증가)
};


(q, r) → 실제 타일맵 좌표 변환 (HexToWorldOffset)

Flat-Top 육각형을 유니티의 사각형 타일맵에 정확히 배치하려면, 좌표 변환 공식이 필요하다.

float x = q * (maxWidth * 0.75f); // q(열)에 따라 가로 간격을 조절
float y = r * height;             // r(행)에 따라 세로 위치 조절

// Flat-Top 육각형의 핵심 오프셋!
if (q > 0) y += height * 0.5f; // q가 양수(오른쪽)면 y를 반 칸 올리고
else if (q < 0) y -= height * 0.5f; // q가 음수(왼쪽)면 y를 반 칸 내린다.


이 y 보정(offset) 부분이 Flat-Top 육각형 배치의 핵심이었다. q 값에 따라 y축 위치를 반 칸씩 조절해줘야 육각형들이 엇갈리면서 자연스럽게 붙는다. 처음엔 이 부분을 이해하는 데 시간이 좀 걸렸지만, 한번 이해하고 나니 육각형 배치가 훨씬 수월해졌다.

맵 생성 흐름은 이렇게 진행했다!

중심 육각형 생성:

맵의 시작점인 (0, 0)에 중심 육각형을 만들고, 일단 'Plains(들판)' 지형으로 고정했다. 여기가 플레이어가 처음 시작할 곳이다.


주변 6방향 육각형 배치
HexGridHelper.Directions를 이용해서 중심 육각형의 주변 6방향에 인접한 육각형 좌표들을 계산했다.
이미 맵에 존재하는 좌표는 건너뛰고, 새로운 좌표에만 지형을 지정했다.
지형은 'Forest(숲)', 'Plains(들판)', 'Quarry(채석장)' 세 가지 중 하나를 랜덤으로 지정했다.
foreach (var dir in HexGridHelper.Directions)

{
    Vector2Int next = center + dir; // 중심 육각형에서 각 방향으로 이동한 다음 육각형 좌표
    if (!mapData.Contains(next)) // 이미 맵에 존재하지 않는 좌표라면
    {
        // TerrainType 열거형을 이용해 랜덤으로 지형을 선택했다.
        TerrainType random = (TerrainType)Random.Range(0, 3); // Plains~Quarry (0, 1, 2)
        mapData.SetTerrain(next, random); // 맵 데이터에 지형 정보를 저장했다.
    }
}


 

타일맵 상에 그리기

각 육각형은 실제로는 사각형 타일들을 조합해서 육각형처럼 보이게 만들었다.
타일 크기는 minWidth, maxWidth, height로 미리 정의해두었다.
각 육각형 위치에 저장된 TerrainType에 따라 미리 준비된 타일 스프라이트들을 선택해서 유니티 타일맵에 그렸다.

 

실제 결과물은 어땠냐면?

총 7개의 육각형이 맵에 배치되었다. (중앙 1개 + 주변 6방향 6개)
각 육각형의 지형은 내가 의도한 대로 'Forest', 'Plains', 'Quarry' 중 다양하게 랜덤 생성되었다.
가장 만족스러웠던 점은 육각형들 간의 간격이나 위치 오차 없이 매우 자연스럽게 이어지는 모습을 완성했다는 것이다.


🤔 내가 느낀 점

Flat-Top 좌표계의 y 보정(offset)을 이해하는 데 시간이 조금 걸렸지만, 한번 정확히 정리해놓고 나니 육각형 배치가 훨씬 수월해졌다. 육각형을 실제 타일맵에 뿌리는 방식은 마치 커다란 타원형 마스크로 타일을 잘라낸 것처럼 느껴졌다. 이런 방식이 유연해서 나중에 육각형으로 된 다양한 형태의 맵을 만들 때도 유용하게 쓸 수 있을 것 같다.
각 육각형에 지형 데이터를 매핑하는 구조(HexMapData)를 깔끔하게 분리해둔 덕분에 유지보수가 쉬울 것 같다는 생각이 들었다. 데이터를 따로 관리하니 코드가 훨씬 간결해졌다.


✅ 다음 목표는?

이번에 만든 육각형 외형 위에 구불구불한 길을 타일 형태로 시각적으로 생성하는 것을 목표로 삼았다.
길은 1~5칸 정도의 두께로 자연스럽게 이어지도록 구현할 예정이다.
길 타일은 기존 지형 타일맵과 별도의 roadTilemap에 적용해서 관리할 계획이다.

 

 

 

 

최근댓글

최근글

skin by © 2024 ttuttak