A* Pathfinding Project에서 로컬 회피 기능을 사용하는 방법.
A* Pathfinding Project에 포함된 로컬 회피 기능은 RVO(Reciprocal Velocity Obstacles )를 기반으로 하며, 특히 ORCA 알고리즘을 사용합니다. 이는 예를 들어 주변을 이동하는 인간 에이전트에게 잘 맞습니다. 빠르게 속도를 변경할 수 없는 차량에는 적합하지 않습니다.
A* Pro 기능 이 기능은 A* Pathfinding Project의 Pro 버전에서만 사용할 수 있습니다. 이 함수/클래스/변수는 A* Pathfinding Project의 무료 버전에는 존재하지 않거나 기능이 제한될 수 있습니다. Pro 버전은 여기에서 구매할 수 있습니다. |
Overview
로컬 회피는 에이전트들이 서로를 피하도록 하며, 더 간단한 동적 장애물 회피에도 사용됩니다. 경로 탐색과는 달리, 로컬 회피는 이름 그대로 매우 로컬합니다. 에이전트와 가까운 장애물만을 고려하며, 에이전트의 글로벌 목표는 신경 쓰지 않습니다. 따라서 큰 장애물을 피할 수 없습니다.
반면에, 작은 동적 객체를 경로 탐색으로 처리하는 것은 비효율적일 수 있습니다. 작은 변화마다 경로를 재계산해야 하기 때문입니다. 로컬 회피는 이런 경우에 훨씬 더 적합합니다.
RVO 시스템은 두 부분으로 나뉩니다. 첫 번째는 핵심 시뮬레이션 코드로, GameObjects와 MonoBehaviours와 같은 Unity 특정 객체와는 완전히 독립적입니다. 이 핵심 코드는 모든 로컬 회피 에이전트의 시뮬레이션을 처리합니다.
두 번째 부분은 Unity 인터페이스입니다. 이 클래스들 중 많은 부분이 해당 핵심 클래스의 래퍼(wrapper)입니다. 예를 들어, RVOController 클래스는 Pathfinding.RVO.IAgent 인터페이스의 래퍼 클래스입니다.
Examples
로컬 회피 시스템이 어떻게 작동하는지 보여주는 세 개의 예제 장면이 포함되어 있습니다. 이 장면들은
Assets/AstarPathfindingProject/ExampleScenes/Example11_RVO 및
Assets/AstarPathfindingProject/ExampleScenes/Example16_RVO 2D에서 찾을 수 있습니다.
Integration
장면에는 RVOSimulator 컴포넌트가 정확히 하나 있어야 합니다. 다른 모든 컴포넌트는 이 컴포넌트를 자동으로 찾습니다.
에이전트 설정 방법은 사용하는 이동 스크립트에 따라 다릅니다.
FollowerEntity 이동 스크립트를 사용하는 경우, 이동 스크립트에서 Local Avoidance 체크박스를 활성화할 수 있습니다.
AIPath , RichAI 또는 사용자 정의 이동 스크립트를 사용하는 경우, 에이전트에 RVOController 컴포넌트를 추가해야 합니다.
참고 AILerp 이동 스크립트는 경로를 따라 보간(interpolate)하며 정확하게 따르도록 설계되었습니다. 따라서 경로에서 벗어나는 것이 의미가 없으므로 로컬 회피를 지원하지 않습니다. |
참조 사용자 정의 이동 스크립트에서 로컬 회피를 사용하거나 로컬 회피가 어떻게 작동하는지 더 깊이 배우고 싶다면, 다음 페이지를 참조하세요: Custom Local Avoidance Movement Script . |
이 두 가지 방법은 본질적으로 동일한 설정을 노출합니다. FollowerEntity 는 내부적으로 Entity Component System(ECS)을 사용하므로 RVOController 를 별도의 컴포넌트로 사용할 수 없습니다. 내부적으로는 둘 다 동일한 방식으로 시뮬레이션됩니다.
Radius
에이전트의 반경을 월드 유닛으로 나타냅니다.
참고 AIPath/RichAI/AILerp와 같은 이동 스크립트( IAstarAI 인터페이스를 구현하는 모든 것)가 동일한 GameObject에 부착된 경우, 이 값은 해당 스크립트에 의해 제어됩니다. |
참조 이 설정은 Pathfinding.RVO.RVOController.radius 멤버에 해당합니다. |
Height
에이전트의 높이를 월드 유닛으로 나타냅니다.
참고 AIPath/RichAI/AILerp와 같은 이동 스크립트( IAstarAI 인터페이스를 구현하는 모든 것)가 동일한 GameObject에 부착된 경우, 이 값은 해당 스크립트에 의해 제어됩니다. |
참조 이 설정은 Pathfinding.RVO.RVOController.height 멤버에 해당합니다. |
Center
이 게임 오브젝트의 피벗 포인트를 기준으로 한 에이전트의 중심입니다.
참고 AIPath/RichAI/AILerp와 같은 이동 스크립트( IAstarAI 인터페이스를 구현하는 모든 것)가 동일한 GameObject에 부착된 경우, 이 값은 해당 스크립트에 의해 제어됩니다. |
참조 이 설정은 Pathfinding.RVO.RVOController.center 멤버에 해당합니다. |
Agent Time Horizon
다른 에이전트와의 충돌을 예측하기 위해 미래를 얼마나 멀리 내다볼지 설정합니다 (초 단위).
참조: 이 설정은 Pathfinding.RVO.RVOController.obstacleTimeHorizon 멤버에 해당합니다. |
Obstacle Time Horizon
장애물과의 충돌을 예측하기 위해 미래를 얼마나 멀리 내다볼지 설정합니다 (초 단위).
참조: 이 설정은 Pathfinding.RVO.RVOController.obstacleTimeHorizon 멤버에 해당합니다. |
Max Neighbours
고려할 다른 에이전트의 최대 수를 설정합니다.
값이 작을수록 CPU 부하가 줄어들며, 값이 클수록 로컬 회피 품질이 향상될 수 있습니다.
참조: 이 설정은 Pathfinding.RVO.RVOController.maxNeighbours 멤버에 해당합니다. |
Layer
이 에이전트의 회피 레이어를 지정합니다.
다른 에이전트의 collidesWith 마스크가 이 에이전트를 피할지 여부를 결정합니다.
참조: 이 설정은 Pathfinding.RVO.RVOController.layer 멤버에 해당합니다. |
Collides with
이 에이전트가 피할 레이어 마스크를 지정합니다.
다음과 같이 설정할 수 있습니다: `CollidesWith = RVOLayer.DefaultAgent | RVOLayer.Layer3 | RVOLayer.Layer6 ...`
이것은 여러 팀이 있는 게임에서 매우 유용합니다. 예를 들어, 한 팀의 에이전트들은 서로를 피해야 하지만 적을 피하지 않기를 원할 수 있습니다.
이 필드는 이 에이전트가 피할 다른 에이전트에만 영향을 미치며, 다른 에이전트가 이 에이전트에 어떻게 반응하는지에는 영향을 미치지 않습니다.
참조: Bitmask Tutorial http://en.wikipedia.org/wiki/Mask_(computing) |
참조: 이 설정은 Pathfinding.RVO.RVOController.collidesWith 멤버에 해당합니다. |
Priority
다른 에이전트가 이 에이전트를 얼마나 강하게 피할지를 설정합니다.
일반적으로 0과 1 사이의 값을 가집니다. 비슷한 우선순위를 가진 에이전트들은 서로를 동일한 강도로 피합니다. 만약 에이전트가 자신보다 높은 우선순위를 가진 다른 에이전트를 보면, 그 에이전트를 더 강하게 피합니다. 극단적인 경우 (예: 이 에이전트의 우선순위가 0이고 다른 에이전트의 우선순위가 1인 경우) 그 다른 에이전트를 움직이는 장애물로 취급합니다. 반대로 에이전트가 자신보다 낮은 우선순위를 가진 다른 에이전트를 보면, 그 에이전트를 덜 피합니다.
일반적으로 이 에이전트의 회피 강도는 다음과 같습니다:
if this.priority > 0 or other.priority > 0:
avoidanceStrength = other.priority / (this.priority + other.priority);
else:
avoidanceStrength = 0.5
참조: 이 설정은 Pathfinding.RVO.RVOController.priority 멤버에 해당합니다. |
Lock When Not Moving
원하는 속도가 거의 0일 때 자동으로 locked (true)으로 설정합니다.
이것은 다른 유닛들이 이 유닛을 밀어내는 것을 방지합니다 (예: 좁은 통로를 막을 때).
이 설정이 true일 때 SetTarget 이나 Move 를 호출하면, 원하는 속도가 0이 아니면 locked 필드가 true로, 0이면 false로 설정됩니다.
참조: 이 설정은 Pathfinding.RVO.RVOController.lockWhenNotMoving 멤버에 해당합니다. |
Locked
잠긴 유닛은 움직일 수 없습니다.
다른 유닛들은 여전히 이 유닛을 피하지만, 회피 품질은 최적이 아닙니다.
참조: 이 설정은 Pathfinding.RVO.RVOController.locked 멤버에 해당합니다. |
Debug
장면 뷰에서 디버그 정보를 그리도록 설정합니다.
참조: 이 설정은 Pathfinding.RVO.RVOController.debug 멤버에 해당합니다. |
Integration with physics
종종 에이전트에 콜라이더를 추가하고 싶을 수 있습니다, 예를 들어 총알에 맞게 하기 위해서입니다. 하지만 단순히 에이전트에 콜라이더(와 리지드바디)를 추가하면, 혼잡한 시나리오에서 로컬 회피 품질이 크게 떨어질 수 있습니다. 이는 매우 혼잡할 때 에이전트들이 약간 겹칠 수 있기 때문인데, 물리 시스템은 이를 방지하지만 로컬 회피 시스템만큼 효율적으로 하지 못해 더 나쁜 움직임을 초래할 수 있습니다.
에이전트에 콜라이더가 있는 경우, 에이전트 간의 충돌을 비활성화하는 것을 권장합니다. 에이전트를 별도의 레이어에 배치하고, Unity 물리 설정에서 해당 레이어와의 충돌을 비활성화할 수 있습니다. Unity에는 2D와 3D 물리에 대한 별도의 설정이 있으므로 올바른 설정을 변경해야 합니다.
Keeping the agents on the navmesh
로컬 회피를 사용할 때 에이전트가 다른 에이전트를 그래프 밖으로 밀어내는 경우를 쉽게 발견할 수 있습니다. 이는 일반적으로 바람직하지 않지만, 다행히도 이를 완화할 방법이 있습니다.
RVOSimulator.useNavmeshAsObstacle 옵션을 활성화하여 로컬 회피 시스템이 네브메쉬를 장애물로 처리하도록 할 수 있습니다. 이는 성능에 영향을 미치지만, 에이전트가 그래프 밖으로 밀려날 가능성을 크게 줄입니다.
에이전트는 로컬 회피를 사용하지 않고도 그래프에 자신을 제한할 수 있습니다. RichAI 나 FollowerEntity 이동 스크립트를 사용하는 경우, 이동 처리 방식 때문에 자동으로 그래프에 제한됩니다. AIPath 스크립트를 사용하는 경우 constrainInsideGraph 옵션을 사용하여 이를 활성화할 수 있습니다. 그러나 로컬 회피 에이전트가 네브메쉬를 인식하지 않는 경우( RVOSimulator.useNavmeshAsObstacle 옵션 사용), 그래프 밖으로 이동하려고 시도할 수 있으며, 이는 그래프 가장자리에서 불안정한 움직임을 초래할 수 있습니다. 특히 혼잡한 시나리오에서 그러합니다.
아래 이미지는 여러 에이전트가 로컬 회피를 사용하여 코너의 동일한 지점으로 이동하도록 명령받았을 때의 상황을 보여줍니다. 에이전트를 그래프에 제한하지 않으면 쉽게 장애물 안으로 밀려나는 것을 볼 수 있습니다.
Performance
로컬 회피의 성능은 매우 높습니다. 특히 로컬 회피 시뮬레이션이 매우 높은 FPS로 실행될 필요가 없다는 점을 고려하면 더욱 그렇습니다. 높은 FPS로 실행해도 품질이 크게 향상되지 않기 때문에 CPU 사이클의 낭비일 뿐입니다. 저의 경우, 상당히 강력한 컴퓨터(i9 프로세서, 16코어)에서 30,000개의 에이전트를 좋은 FPS로 시뮬레이션할 수 있었습니다. 로컬 회피 시뮬레이션은 고정된 60 FPS로 실행되었고, 게임은 몇 백 FPS로 실행되었으며, 최저 FPS는 약 60이었습니다. 이 시뮬레이션의 시각화는 각 에이전트에 대한 하나의 사각형을 포함한 메쉬를 생성함으로써 단순하게 처리되었습니다. 이렇게 많은 숫자의 게임 오브젝트를 생성하는 것은 매우 느리기 때문에, 각 에이전트에 대해 게임 오브젝트를 생성하는 것은 적합하지 않았습니다. 테스트했을 때 이렇게 많은 게임 오브젝트를 생성하는 데 약 10초가 소요되었습니다. 에이전트는 원형으로 배치되었으며, 각 에이전트는 원의 반대편 지점으로 이동하려고 시도했습니다. 따라서 가능한 한 혼잡한 상황이었습니다.
그러나 게임에서 이처럼 많은 수의 에이전트를 가질 수 있다고 기대하지 마세요. 위의 테스트는 매우 경량이었고, 실제 게임에서는 많은 다른 요소들로 인해 많은 오버헤드가 발생합니다.
위의 이미지는 앞서 언급한 스트레스 테스트를 보여줍니다.
참조 example_local_avoidance_lightweight |
Local Avoidance Priorities
모든 에이전트가 동일하지만, 어떤 에이전트는 더 중요할 수 있습니다. 여기서 우선순위가 등장합니다. 에이전트가 더 높은 우선순위를 가지면, 다른 에이전트는 이를 더 피하려고 시도하고, 해당 에이전트는 다른 에이전트를 덜 피하려고 시도합니다.
일반적으로, 한 에이전트가 우선순위 A를 가지고 있고 다른 에이전트가 우선순위 B를 가지고 있다면, A는 충돌을 피하기 위한 책임의 이 비율을 차지합니다:
B / (A + B)
예를 들어, A가 우선순위 1을 가지고 있고 B가 우선순위 2를 가지고 있다면, A는 충돌 회피 책임의 2/3를 차지하고 B는 1/3를 차지하게 됩니다.
Local Avoidance Layers
에이전트 레이어와 회피할 레이어를 변경하여 일부 에이전트가 다른 캐릭터를 완전히 무시하도록 할 수 있습니다. 각 RVOController 에는 자신의 레이어와 다른 에이전트를 회피할 때 고려할 레이어를 지정하는 필드가 있습니다. 이 레이어는 Unity 레이어와는 다릅니다. 이들은 순수하게 로컬 회피에만 사용됩니다.
Layer
이 에이전트의 회피 레이어를 지정합니다.
Collides With
이 에이전트가 회피할 레이어 마스크를 지정합니다.
두 팀이 있는 게임을 만들 때 매우 유용합니다. 이런 경우에는 서로 다른 팀의 에이전트들이 서로를 회피하지 않도록 설정하고 싶을 수 있습니다. 팀 A의 모든 에이전트를 한 레이어에, 팀 B의 모든 에이전트를 다른 레이어에 배치한 다음, 에이전트가 자신의 팀 레이어에 있는 에이전트만 회피하도록 설정할 수 있습니다.
Player Characters
플레이어 캐릭터가 로컬 회피의 영향을 받지 않도록 설정하고 싶지만, 다른 에이전트가 플레이어를 회피하도록 하고 싶을 수 있습니다. 이는 RVOController 컴포넌트의 속도를 수동으로 설정하여 할 수 있습니다.
void Update () {
var x = Input.GetAxis("Horizontal");
var y = Input.GetAxis("Vertical");
var v = new Vector3(x, 0, y) * speed;
// RVOController의 속도를 재정의합니다. 이렇게 하면 한 시뮬레이션 단계 동안 로컬 회피 계산이 비활성화됩니다.
rvo.velocity = v;
transform.position += v * Time.deltaTime;
}
이렇게 하면 로컬 회피 시스템이 해당 에이전트에 대한 계산을 완전히 건너뛰고, 대신 설정한 속도로 이동한다고 가정합니다. 이는 장애물이 작아서 경로 탐색이 필요하지 않으며, 원형이어도 괜찮은 경우 이동 장애물에도 유용할 수 있습니다.
ManualRVOAgent.cs 라는 포함된 샘플 스크립트가 있으며, 이를 통해 기존의 캐릭터 이동 스크립트를 로컬 회피와 통합하는 방법을 보여줍니다.
'유니티 에셋 > A* Pathfinding project pro' 카테고리의 다른 글
Wandering AI Tutorial (0) | 2024.05.25 |
---|---|
Local Avoidance > Custom Local Avoidance Movement Script (0) | 2024.05.25 |
Using Modifiers (0) | 2024.05.24 |
Searching for paths (0) | 2024.05.24 |
Movement scripts > Writing a movement script (0) | 2024.05.24 |