Godot 4 유닛 이동 — 네비게이션 구조 잡기와 Avoidance 제대로 쓰기
Tiny Reign은 자동 전투 게임이라 유닛 여러 개가 동시에 움직여야 했다. 처음엔 길찾기만 붙이면 끝나는 줄 알았는데, 실제로는 두 단계 문제였다. 하나는 맵 위에서 경로를 찾는 것이고, 다른 하나는 같은 목표로 몰려가는 유닛들이 서로 겹치지 않게 하는 것이다. 둘 다 Godot에 기능이 내장돼 있는데, 둘 다 그냥 켜서는 제대로 동작하지 않았다.
타일맵 내장 네비게이션의 한계
처음엔 타일맵 내장 네비게이션을 썼다. TileMap에 네비게이션 레이어를 추가하고 NavigationAgent2D를 붙이면 되니까 간단해 보였다. 그런데 써보니 문제가 하나씩 나왔다.
타일마다 네비게이션 폴리곤을 직접 설정해야 한다. 타일셋에서 각 타일을 골라 네비게이션 영역을 그려줘야 하는데, 타일 종류가 많아지면 이게 꽤 번거롭다. 그리고 타일맵을 수정할 때마다 네비게이션이 제대로 반영됐는지 확인해야 했다.
더 큰 문제는 Y-sort와 함께 쓸 때였다. 유닛이 Y축 기준으로 렌더링 순서가 바뀌어야 하는데, 타일맵 내장 네비게이션에서 이 부분이 예상대로 안 됐다. 레이어 구조를 맞추는 게 복잡했다.
NavigationRegion2D로 분리했다

방법은 생각보다 단순하다. NavigationRegion2D 노드를 씬에 추가하고 NavigationPolygon을 직접 그려주면 된다. 타일맵과 분리된 별도 노드니까 타일맵을 바꿔도 네비게이션은 독립적으로 유지된다.
에디터에서 폴리곤을 직접 그리는 방식이라 “이 지역은 못 지나간다”를 시각적으로 확인하면서 설정할 수 있다. 장애물 영역을 StaticBody2D 대신 NavigationObstacle2D로 추가하는 것도 직관적이었다.
바꾸면서 씬 구조도 정리했다. TileMap은 렌더링 전용으로 두고, 네비게이션은 완전히 별도 레이어로 분리했다. 유닛들은 Y-sort가 활성화된 노드 아래에 배치해서 Y축 기준 렌더링 순서가 자동으로 적용되도록 했다.
이렇게 하니까 타일맵 위를 걷는 유닛이 타일 뒤에 가려지거나 앞에 나오는 게 자연스럽게 됐다. 2D 게임에서 2.5D 느낌을 내려면 Y-sort가 핵심인데, 네비게이션을 분리하고 나서야 제대로 동작했다.
길은 찾는데 다 겹쳐버린다
경로 문제가 풀리니 다음 문제가 나왔다. 유닛 여러 개가 같은 목표로 이동하면 전부 한 자리에 겹쳐버린다. 길찾기만으로는 부족하고 서로 밀어내는 처리가 필요하다.
Godot의 NavigationAgent2D에는 Avoidance 기능이 내장되어 있다. 여기서도 처음엔 그냥 켜면 되는 줄 알았다.
Avoidance는 켜는 것만으로 안 된다

avoidance_enabled = true로 설정하면 끝인 줄 알았는데, 실제로 켰더니 유닛들이 이상하게 진동하거나 벽에 끼는 문제가 생겼다. 이유를 찾아보니 몇 가지를 같이 설정해야 했다.
먼저 유닛마다 사각형 콜리전 대신 원형 콜리전을 써야 Avoidance 계산이 제대로 된다. Avoidance는 에이전트를 원형으로 취급하기 때문에 사각형 콜리전과 계산이 맞지 않는다. 반지름 값도 실제 유닛 크기에 맞게 조정해야 했다.
velocity를 직접 넘겨야 한다
NavigationAgent2D에서 avoidance를 쓰면 velocity_computed 시그널이 생긴다. 이걸 연결해서 계산된 velocity를 받아 써야 한다. 내가 계산한 방향으로 바로 이동시키면 avoidance가 무시된다.
흐름은 이렇다. 목표 지점 방향으로 velocity를 계산하고 NavigationAgent2D.set_velocity()로 넘긴다. 그러면 avoidance 계산이 돌아가고 velocity_computed 시그널로 보정된 velocity가 돌아온다. 그걸 CharacterBody2D.velocity에 넣고 move_and_slide()를 호출한다.
여기서 순서를 헷갈리면 avoidance를 켜놓고도 효과가 없다. 계산을 요청하는 쪽과 결과를 받아 쓰는 쪽이 분리돼 있다는 걸 이해하는 게 핵심이었다.
motion_mode와 초기 target_position
CharacterBody2D의 motion_mode를 FLOATING으로 바꿔야 했다. 기본값인 GROUNDED 모드에서는 중력 방향 처리가 개입해서 2D 탑다운 게임에서 유닛이 이상하게 움직인다. FLOATING으로 바꾸고 나서야 안정적으로 됐다.
그리고 게임 시작 시점에 NavigationAgent2D의 초기 target_position을 유닛 자신의 위치로 설정해줘야 한다. 안 하면 첫 프레임에 (0, 0)으로 이동하려는 경로가 잡혀서 유닛이 맵 구석으로 튀어가는 버그가 생긴다. 이건 원인을 찾는 데 시간이 꽤 걸렸다. 게임을 켜자마자 유닛들이 좌측 상단으로 우르르 몰려가는 걸 보고 길찾기가 깨진 줄 알았는데, 실제로는 목표 지점이 설정되기 전 한 프레임 동안 기본값이 적용된 것이었다.
정리하면
두 문제 모두 결론이 비슷했다. 엔진이 기능을 제공한다고 해서 켜기만 하면 되는 게 아니었다. 네비게이션은 타일맵에서 분리해야 구조가 깨끗해졌고, Avoidance는 원형 콜리전과 velocity_computed 시그널, FLOATING 모드까지 세 가지를 챙겨야 동작했다.
돌아보면 초반에 구조를 제대로 잡는 게 나중에 고치는 것보다 훨씬 덜 힘들었다. 타일맵 내장 방식으로 계속 갔다면 맵을 수정할 때마다 네비게이션을 다시 확인해야 했을 거고, Y-sort는 끝까지 어색하게 남았을 것이다. 프로토타입 자체는 나중에 접었지만 이때 잡아둔 이동 구조는 마지막까지 손댈 일이 없었다.



답글 남기기