방황하는 AI를 만드는 방법에 대한 튜토리얼.
방황하는 AI를 만드는 방법에는 여러 가지가 있습니다. 이 방법들은 품질과 성능 면에서 차이가 있습니다. 명확한 최선의 해결책은 없으며, 각 방법은 게임에 따라 더 적합할 수 있습니다. 각 방법에 대해 장점과 단점을 나열했습니다.
- 방법 1: 원 안의 무작위 지점
- 방법 2: 전체 그래프에서 무작위 노드
- 방법 3: RandomPath 유형
- 방법 4: ConstantPath 유형
- 방법 5: 너비 우선 탐색
방법 1: 원 안의 무작위 지점
이 방법은 캐릭터 주위의 특정 거리 내에서 무작위 지점을 선택하는 가장 간단한 방법입니다.
방법 1: 원 안의 무작위 지점
이동할 지점을 선택하는 가장 쉬운 방법은 캐릭터 주위의 특정 거리 내에서 무작위 지점을 선택하는 것입니다.
Vector3 PickRandomPoint () {
var point = Random.insideUnitSphere * radius;
point.y = 0;
point += transform.position;
return point;
}
포함된 이동 스크립트 중 하나를 사용하는 경우, 예를 들어 AI가 현재 경로의 끝에 도달할 때마다 목적지를 변경할 수 있습니다.
using UnityEngine;
using System.Collections;
using Pathfinding;
public class WanderingDestinationSetter : MonoBehaviour {
public float radius = 20;
IAstarAI ai;
void Start () {
ai = GetComponent<IAstarAI>();
}
Vector3 PickRandomPoint () {
var point = Random.insideUnitSphere * radius;
point.y = 0;
point += ai.position;
return point;
}
void Update () {
// AI의 목적지를 업데이트합니다.
// AI가 경로를 계산 중이 아니고
// AI가 경로의 끝에 도달했거나 경로가 전혀 없을 때
if (!ai.pathPending && (ai.reachedEndOfPath || !ai.hasPath)) {
ai.destination = PickRandomPoint();
ai.SearchPath();
}
}
}
위의 스크립트를 AIPath, RichAI, AILerp 등의 내장 이동 스크립트가 포함된 어떤 게임 오브젝트에든 부착할 수 있습니다.
참고 AI의 목적지를 설정하려고 시도하는 다른 컴포넌트( AIDestinationSetter 컴포넌트 등)가 부착되어 있지 않은지 확인하십시오. |
참조 Pathfinding.IAstarAI Random.insideUnitSphere |
장점
- 매우 빠름.
- 간단한 코드.
- 모든 그래프 유형에서 비교적 잘 작동함.
단점
- 지점까지의 실제 거리를 무시하므로, 세계가 매우 개방적이지 않은 경우 매우 긴 경로를 생성할 수 있음(위의 비디오 참조).
- 장애물 내부에서 생성된 지점은 그래프의 가장 가까운 지점으로 이동해야 하므로, 벽이나 장애물 근처의 지점으로 약간의 편향이 있음.
기타
- 지점을 선택할 때 패널티를 무시함(그러나 지점까지 경로를 찾을 때는 패널티가 고려됨).
방법 2: 전체 그래프에서 무작위 노드
때로는 매우 간단한 것을 원할 때가 있습니다. 현재 위치 주위의 노드를 선택하는 대신, 그래프에서 무작위 노드를 선택하고 해당 노드의 위치로 이동하려고 시도할 수 있습니다.
GraphNode randomNode;
// 그리드 그래프의 경우
var grid = AstarPath.active.data.gridGraph;
randomNode = grid.nodes[Random.Range(0, grid.nodes.Length)];
// 포인트 그래프의 경우
var pointGraph = AstarPath.active.data.pointGraph;
randomNode = pointGraph.nodes[Random.Range(0, pointGraph.nodes.Length)];
// 모든 그래프 유형에서 작동하지만 훨씬 느림
var graph = AstarPath.active.data.graphs[0];
// 그래프의 모든 노드를 리스트에 추가
List<GraphNode> nodes = new List<GraphNode>();
graph.GetNodes((System.Action<GraphNode>)nodes.Add);
randomNode = nodes[Random.Range(0, nodes.Count)];
// 예를 들어 노드의 중심을 목적지로 사용
var destination1 = (Vector3)randomNode.position;
// 또는 노드 표면의 무작위 지점을 목적지로 사용
// 이는 노드가 큰 네비게이션 메시 기반 그래프에서 유용함
var destination2 = randomNode.RandomPointOnSurface();
사용 용도에 따라, 노드를 선택한 후 다음과 같은 체크를 수행할 수 있습니다:
- 노드가 걷기 가능한지 확인 ( Pathfinding.GraphNode.Walkable 참조)
- 노드가 AI로부터 도달 가능한지 확인 ( Pathfinding.PathUtilities.IsPathPossible 참조)
- 게임에 관련된 다른 체크 사항. 이 체크 중 하나가 실패하면 새로운 무작위 노드를 선택하고 다시 체크를 수행할 수 있습니다. 유효한 노드가 매우 드물지 않은 한 유효한 노드를 빠르게 찾을 수 있을 것입니다.
장점
- 매우 빠름 (위의 일반적인 접근 방식을 사용하지 않는 경우)
- 간단한 코드
단점
- 경로 길이에 대한 제어가 거의 없음
기타
- 지점을 선택할 때 패널티를 무시함 (그러나 지점까지 경로를 찾을 때는 패널티가 고려됨).
방법 3: RandomPath 유형
RandomPath 유형은 특정 지점에서 다른 특정 지점으로의 경로를 계산하는 일반적인 경로와는 달리, 시작 지점에서부터 랜덤하게 멀어지는 경로를 계산하는 경로 유형입니다.
내장된 이동 스크립트 중 하나를 사용하고 있다면 ( Movement scripts 참조) 다음과 같은 작업을 수행할 수 있습니다:
// 에이전트의 내부 자동 경로 재계산을 비활성화합니다
ai.canSearch = false;
RandomPath path = RandomPath.Construct(transform.position, searchLength);
path.spread = spread;
// 에이전트에게 계산하고 따라갈 경로를 지정합니다
ai.SetPath(path);
사용자 정의 이동 스크립트를 사용하는 경우, 이를 수동으로 시커(Seeker)에 전달해야 합니다.
RandomPath path = RandomPath.Construct(transform.position, searchLength);
path.spread = spread;
seeker.StartPath(path);
Searching for paths 참조 |
장점
- 한 번에 지점을 선택하고 경로를 계산함.
- 고려되는 모든 노드가 동일한 확률로 선택되므로, 방법 1: 원 안의 무작위 지점과 달리 장애물 근처에 대한 편향이 없음.
- 최소 경로 비용을 설정할 수 있음(searchLength).
단점
- 긴 경로의 경우 속도가 느릴 수 있음.
- 모든 노드가 동일한 확률로 선택되므로, 디테일이 많은 지역(더 많고 작은 노드가 있는 지역)에 대한 편향이 발생하여 navmesh/recast 그래프에서 잘 작동하지 않음.
기타
패널티를 고려함.
Pathfinding.RandomPath 참조 |
방법 4: ConstantPath 유형
ConstantPath 유형은 시작 지점에서 특정 값과 같거나 그 이하의 비용으로 도달할 수 있는 모든 노드를 찾는 경로 유형입니다. 이러한 모든 노드를 찾은 후, 해당 노드 표면에서 하나 이상의 무작위 지점을 선택할 수 있습니다.
ConstantPath path = ConstantPath.Construct(transform.position, searchLength);
AstarPath.StartPath(path);
path.BlockUntilCalculated();
var singleRandomPoint = PathUtilities.GetPointsOnNodes(path.allNodes, 1)[0];
var multipleRandomPoints = PathUtilities.GetPointsOnNodes(path.allNodes, 100);
내장된 이동 스크립트에 이를 적용하려면, 방법 1: 원 안의 무작위 지점에서 사용한 것과 동일한 접근 방식을 사용할 수 있습니다.
참조 Pathfinding.ConstantPath Pathfinding.PathUtilities.GetPointsOnNodes |
장점
- 단일 경로 요청으로 여러 무작위 지점을 선택할 수 있음.
- 노드를 선택할 확률은 해당 노드의 표면적에 비례함 (따라서 모든 노드가 동일한 크기를 갖는 경우, 예를 들어 그리드 그래프를 사용할 때, 고려되는 모든 노드는 동일한 확률로 선택됨). 방법 1: 원 안의 무작위 지점처럼 장애물 근처에 대한 편향이 없음.
- 필요할 경우 노드의 사용자 정의 필터링이 가능함.
- 방법 3: RandomPath 유형보다 보통 약간 더 빠름.
- navmesh/recast 그래프에서 비교적 잘 작동함.
단점
- 많은 노드를 검색해야 할 때 느릴 수 있음.
기타
- 패널티를 고려함.
방법 5: 너비 우선 탐색
breadth-first search은 시작 노드에서부터 하나의 노드씩 외부로 검색하는 간단한 탐색 방법입니다. 패널티나 다른 유형의 비용을 고려하지 않습니다. 예를 들어, 그리드 그래프에서 대각선으로 이동하는 것이 4개의 비대각선 연결 중 하나를 따라 이동하는 것과 동일한 비용을 가진다는 의미입니다.
사용 방법은 방법 4: ConstantPath 유형에서 사용된 것과 매우 유사합니다.
// 가장 가까운 걷기 가능한 노드 찾기
var startNode = AstarPath.active.GetNearest(transform.position, NNConstraint.Walkable).node;
var nodes = PathUtilities.BFS(startNode, nodeDistance);
var singleRandomPoint = PathUtilities.GetPointsOnNodes(nodes, 1)[0];
var multipleRandomPoints = PathUtilities.GetPointsOnNodes(nodes, 100);
내장된 이동 스크립트에 이를 적용하려면, 방법 1: 원 안의 무작위 지점에서 사용한 것과 동일한 접근 방식을 사용할 수 있습니다.
참조 Pathfinding.PathUtilities.BFS Pathfinding.PathUtilities.GetPointsOnNodes |
장점
- 하나의 지점뿐만 아니라 여러 무작위 지점을 선택할 수 있음.
- 노드를 선택할 확률은 해당 노드의 표면적에 비례함(따라서 모든 노드가 동일한 크기를 갖는 경우, 예를 들어 그리드 그래프를 사용할 때, 고려되는 모든 노드는 동일한 확률로 선택됨). 방법 1: 원 안의 무작위 지점처럼 장애물 근처에 대한 편향이 없음.
- 필요할 경우 나중에 노드를 사용자 정의 필터링할 수 있음.
- 경로 찾기 시스템으로의 왕복이 필요 없으므로 약간의 오버헤드를 줄일 수 있음.
- 보통 방법 4: ConstantPath 유형과 방법 3: RandomPath 유형보다 약간 더 빠름.
단점
- 많은 노드를 검색해야 할 때 느릴 수 있음.
- 경로 찾기 스레드를 사용하지 않으므로, 이를 Unity 메인 스레드에서 쉽게 오프로드할 수 있는 기능을 잃게 됨.
- navmesh/recast 그래프에서는 특히 잘 작동하지 않음. 노드의 크기와 세부 사항에 따라 경로 길이가 크게 달라지기 때문임(노드가 많고 작을수록 BFS가 멀리 도달하지 못함).
기타
- 패널티나 다른 비용을 고려하지 않으며, 탐색된 노드의 수만 중요함.
'유니티 에셋 > A* Pathfinding project pro' 카테고리의 다른 글
Agent-Specific Pathfinding (0) | 2024.05.25 |
---|---|
Multiple agent types (0) | 2024.05.25 |
Local Avoidance > Custom Local Avoidance Movement Script (0) | 2024.05.25 |
Local Avoidance (0) | 2024.05.25 |
Using Modifiers (0) | 2024.05.24 |