고도 엔진에서 Navigation 사용 방법 - Whitmem
고도 엔진에서 Navigation 사용 방법
Game Development
2025-02-09 23:53 게시 10abff1cbcfac5990a0d

0
0
64
이 페이지는 외부 공간에 무단 복제할 수 없으며 오직 있는 그대로 게시되며 부정확한 내용을 포함할 수 있습니다. 법률이 허용하는 한 가이드 라인에 맞춰 게시 내용을 인용하거나 출처로 표기할 수 있습니다.
This page is not to be distributed to external services; it is provided as is and may contain inaccuracies.
고도 엔진에서 Navigation 사용을 위한 NavigationAgent2D, NavigationRegion2D 사용 방법
Navigation 을 사용해서 적이 움직이는 방향을 자동화하고, 캐릭터가 특정 지점으로 따라가도록 구현할 수 있다. 물론 Navigation 없이도 플레이어 캐릭터가 존재하는 방향 벡터를 향하게끔 Position 을 더하거나, Velocity 를 적용할 수 있으나, 이 경우 다른 물체와 충돌했을 때 그대로 멈춰버리는 문제가 발생할 수 있다.
예를 들어 위 객체와 다른 객체가 충돌하게끔 Collision을 지정한 경우, 아래로 계속 힘을 주면 결국 땅과 부딪혀 이도 저도 움직일 수 없는 상태가 된다. 목적지가 좌 우로 움직인다 한들, 벽에 부딪히며 미끌리기만 한다. 따라서 이런 경우에 내비게이션을 통해 길을 제시할 수 있다.
즉 내비게이션은 유효한 지점을 노드로 찾아 직선 거리를 찾아주는 역할을 한다.
위 사진은 임의로 내비게이션 영역을 지정한 모습이다.
목적지 지점까지 직선 거리로 바로 가는 것이 아니라, 유효한 직선 거리를 여러 갈래로 찾아서 차례로 가는 것을 볼 수 있다.
기본 영역 지정
먼저 기본 영역을 지정하기 위해서 NavigationRegion2D 노드 2D를 생성한다.
NavigationRegion2D 의 인스펙터를 보면 기본 폴리곤이 <비어 있음>이다. 새 폴리곤을 추가해서 어떤 영역이 기본적으로 이동할 수 있는 영역인지 정의해야 한다.
위 뷰포트 메뉴바에서 점 추가 모드로 변경하고, 월드 공간에 객체가 움직일 수 있는 영역을 정의해준다.
현재 상태에서 안에 CharacterBody2D 를 배치하고, 캐릭터가 마우스 커서를 따라오게끔 해보자.
CharacterBody2D 노드를 생성하고 스크립트를 붙인다. 여기서는 C# 언어로 작성한다.
public override void _PhysicsProcess(double delta) { }
PhysicsProcess 재정의된 함수 공간에는 물리 처리들을 정의하면 된다. 기본적으로 스크립트를 생성했으면 중력 가속도만큼 Velocity에 속도를 가하는 코드가 내장된다. 사용자가 키보드로 입력해서 움직이는 것은 아니기 때문에, 키보드 입력 부분은 제거해준다. 중력 가속도 부분만 추가해주겠다.
public override void _PhysicsProcess(double delta){ Vector2 velocity = Velocity; velocity += GetGravity() * (float)delta; Velocity = velocity; MoveAndSlide(); }
그러면 기본적으로 캐릭터 바디를 중력때문에 땅으로 떨어지는데, 저 밑으로 떨어지지 않도록 땅을 추가해준다. 헷갈리면 안되는 것이 우리는 콜리젼 박스를 만든 것이 아니다. Navigation 박스를 만든 것이기 때문에 기본적으로 중력만 적용된 객체는 중력 때문에 나갈 수 있다. 이제 영역을 정의했으니 그 영역 안에서 거리까지 빠른 직선 거리를 찾아내주는 실제 에이전트를 추가해야 한다. 이 객체는 캐릭터 바디 2D 아래에 설치해주면 된다. 이 Agent가 실제로 움직이면서 목적지 거리를 찾아준다.
이 Agent 가 마우스 커서를 목적지로 하여금 따라갈 수 있도록 해보자.
결과적으로 Agent만 움직이는 것이 아니라, CharacterBody2D가 움직이면서 Agent2D도 같이 움직이기 때문에 ChracterBody2D에 스크립트를 붙이고, 이 안에서 Agent에 목적지 경로 찾기 요청을 하면 된다.
즉, NavigationAgent2D 객체는 ChracterBody2D 하위에 있고, CharacterBody2D 스크립트에서 이 Agent2D 객체에 요청을 보내기 위해서, 하위에 있는 Agent2D 객체를 찾거나, CharacterBody2D에 Export를 통해 Agent2D를 직접 받으면 된다. 쨋든 ChracterBody2D 에서 Agent2D 객체가 직접적으로 필요할 뿐이다.
[Export] NavigationAgent2D agent2D;
agent2D 객체 내부에 변수가 있는데, TargetPosition 이라는 멤버 변수가 존재한다. 이 공간에 목적지 글로벌 좌표를 넣어주면 에이전트가 자동으로 연산해서 해당 지점까지의 최단 거리를 계산하고 다음으로 이동하면 되는 직선 지점을 찾아준다. 그것이 agent2D 안에 있는 GetNextPathPosition()이라는 함수이다.
즉 TargetPosition 에 마우스 글로벌 좌표를 넣고, 다음 발견된 최단 거리를 바로 가져오면 된다. 마우스 커서를 글로벌 좌표로 알아내기 위해서 GetGlobalMousePosition()함수를 쓰면 된다.
agent2D.TargetPosition = GetGlobalMousePosition(); Vector2 ps = agent2D.GetNextPathPosition();
즉 ps 라는 벡터2는 해당 마우스 글로벌 좌표까지 가기 위해 발견된 첫 번째 경로의 목적지 전역 좌표이다. 이 좌표로 우선 이동하면, Agent가 알아서 다음 좌표를 반환해준다.
테스트로, 이 객체의 GlobalPosition 을 ps 좌표로 변경해본다.
Vector2 velocity = Velocity; velocity += GetGravity() * (float)delta; Velocity = velocity; agent2D.TargetPosition = GetGlobalMousePosition(); Vector2 ps = agent2D.GetNextPathPosition(); this.GlobalPosition = ps; MoveAndSlide();
그러면 내비게이션 영역 내에서 객체가 움직이고 중력 가속도에 의해 떨어지는 것을 확인할 수 있다. 다만 직접 GlobalPosition을 계속 변경해서 인지 애매모호하게 떨어지는 것을 확인할 수 있다. 이는 중력 가속도는 계속 더해지고 있는데 위치를 강제로 마우스 커서로 계속 수시로 옮기니까 가속도가 늘어나는 만큼 일시적으로 순간이동한 거리가 계속보이는 것이다.
한편, 디버깅할 때 내비게이션 영역을 보이게할 수 있다.
에디터의 디버그 메뉴 -> 내비게이션 보이기 및 어보이던스 보이기를 체크해준다.
이제 한편 마우스가 존재하는 위치로 바로 이동하는 것이 아니라, 속도를 붙여 해당 위치로 속도를 붙여 이동하게끔 해 보자. 매 순간 Velocity 에는 중력가속도가 붙어 계속 증가되는 속도 정보이다. 즉 여기에 임의로 한번 속도를 부여한 이상 해당 속도를 별도로 제거해주지 않는 이상 해당 방향으로 끊임 없이 가게 된다.
따라서 Velocity 값에 중력 가속도는 상시 늘어나되, 좌우 움직이는 속도만 일시적으로 주고 빠질려면, Velocity 를 중력 가속도가 늘어나는 부분과 속도를 일시적으로 주는 부분을 나눠야 한다.
즉 지금은 이전 속도를 가져와서 중력을 더해서 다시 내보내고 있다. 이 작업이 매 프레임마다 반복되면 중력 가속도 값 만큼 계속 속도가 더해지는 것이다. 임의로 해당 스크립트 공간에 전역 Vector2 gravityAccel 이라는 변수를 만들고, 해당 변수에 중력 가속도만큼 계속 더한다.
그리고 velocity는 매 프레임 마다 초기화 해준다. (물론 매 프레임 마다 new 생성자를 호출하는 행위는 매우 안좋다. 여기서는 테스트이니 new로 정의하겠다. 원래는 전역으로 velocity 라는 변수를 하나 만들어 매 프레임 마다 X, Y를 0으로 대입하는 것이 좋다.
아무튼 매 프레임 마다 velocity는 0으로 초기화되고, 전역 gravityAccel 에 중력 가속도만큼 계속 더한다. 그리고 이 캐릭터 바디의 최종 Velocity에 임시적인 velocity 를 대입한다.
여기서 헷갈리면 안되는 것이 Velocity 와 velocity 변수는 서로 다른 변수이다. velocity는 프레임 안에서 임시적으로 대입하기 위해 만든 메모리 공간이고, Velocity 는 ChracterBody2D 에 적용되는 자체 멤버 변수인 것이다.
한편, 땅 바닥에 부딪힌 경우 gravityAccel 은 0으로 초기화 해주자.
Vector2 velocity = new Vector2(); gravityAccel += GetGravity() * (float)delta; velocity = gravityAccel; if (IsOnFloor()) { gravityAccel.Y = 0; }
한편, agent2D 에는 이제 다음 좌표 정보를 가져오고, 현재 플레이어 캐릭터 바디 2D 가 다음 경로를 향하는 방향 벡터를 알아내고, 해당 방향 벡터를 단위화한 뒤 일정 크기만큼 이동하도록 코드를 작성한다.
agent2D.TargetPosition = GetGlobalMousePosition(); Vector2 ps = agent2D.GetNextPathPosition(); Vector2 vc = this.GlobalPosition.DirectionTo(ps).Normalized() * 100; velocity.X += vc.X; velocity.Y += vc.Y;
즉 해당 다음 목적지를 향하는 다음 노드 좌표로 바로 이동하는 것이 아니라, 목적지 좌표를 향하기 위한 노드의 방향을 알아내고, 해당 방향으로 속도 벡터를 만들어, velocity 에 더해서 반영하면 되는 것이다.
Vector2 velocity = new Vector2(); gravityAccel += GetGravity() * (float)delta; velocity = gravityAccel; if (IsOnFloor()) { gravityAccel.Y = 0; } agent2D.TargetPosition = GetGlobalMousePosition(); Vector2 ps = agent2D.GetNextPathPosition(); Vector2 vc = this.GlobalPosition.DirectionTo(ps).Normalized() * 100; velocity.X += vc.X; velocity.Y += vc.Y; GD.Print(velocity); Velocity = velocity; MoveAndSlide();
이러면 이제 마우스 방향대로 캐릭터가 움직이는 것을 볼 수 있다. 마우스 커서가 하늘을 향하면 점프를 방방 뛰는 것을 볼 수 있다.
그리고 마우스 커서가 아무리 밖으로 나가있더라도 경로는 최대 영역까지만 잡히기 때문에 객체는 밖으로 나갈 속도를 받을 수 없다. 물론 다른 외부 힘에 의해 강제로 이동시키는 경우 객체는 밖으로 나갈 수 있다. 이를 막기 위해서는 애초에 테두리를 Collision으로 한번 더 씌우는 등 작업이 필요할 것으로 보인다.
장애물 설치
한편 이 영역안에 장애물을 정의할 수 있다.
장애물을 정의하기 위해서 NavigationRegion2D 노드 하위에 NavigationObstacle2D 라는 노드를 추가해주면 된다.
그리고 NavigationObstacle2D 객체의 Inspector을 보면 NavigationMesh 영역에 Affect Navigation... 영역이 보이는데 이를 활성화해준다. 메시로 처리하게끔 만드는 것이다. 메시로 처리해줘야 NavigationRegion2D에서 베이크로 구워서 해당 메시 영역만 제외할 수 있다. 이제, NavigationRegion2D 로 돌아가서, NavigationPolygon을 구워줘야 한다.
그러면 해당 영역이 제외되는 것을 확인할 수 있다.
그런데 경로를 잘 찾아주는데 경로를 중간으로만 찾아줘서인지 경로가 하늘로 가는 것을 볼 수 있다. 정점 중앙으로 해서 찾아주는 것 같은데, 이러면 곤란하니까, ChracterBody2D 하위에 등록한, 경로를 찾아주는 Agent 노드를 선택한 뒤 경로 전처리 모드를 Corridorfunnel로 변경해준다. 이러면 중앙 지점으로 찾아주는 것이 아니라 모서리로 바로 찾아주는 듯 싶다.
해당 경로로 바로 찾아주는 것을 확인할 수 있다.
그런데 점프 크기가 너무 작아서인지 (즉 Y 벡터가 작고 중력 가속도가 더 커서인지) 이 객체를 넘어가지 못하는데 임의로 Y 벡터만 배율을 줘 본다.
조금 엉성하게 뛰지만 더 점프하면서 뛰는 것을 볼 수 있다. 다음 게시글에서는 이를 좀더 개선해서 업로드하도록 하겠다.
댓글 0개
댓글은 일회용 패스워드가 발급되며 사이트 이용 약관에 동의로 간주됩니다.
확인
Whitmemit 개인 일지 블로그는 개인이 운영하는 정보 공유 공간으로 사용자의 민감한 개인 정보를 직접 요구하거나 요청하지 않습니다. 기본적인 사이트 방문시 처리되는 처리 정보에 대해서는 '사이트 처리 방침'을 참고하십시오. 추가적인 기능의 제공을 위하여 쿠키 정보를 사용하고 있습니다. Whitmemit 에서 처리하는 정보는 식별 용도로 사용되며 기타 글꼴 및 폰트 라이브러리에서 쿠키 정보를 사용할 수 있습니다.
이 자료는 모두 필수 자료로 간주되며, 사이트 이용을 하거나, 탐색하는 경우 동의로 간주합니다.