Modifiers는 경로를 후처리하여 단순화하거나 부드럽게 하는 작은 스크립트입니다.
Modifiers는 확장 가능한 애드온 아키텍처를 사용하여 시스템에 내장되어 있어 자신만의 modifier를 쉽게 추가할 수 있습니다. 이 튜토리얼에서는 프로젝트에 포함된 경로 부드럽게 하는 모듈(Components-> Pathfinding ->Modifiers->SimpleSmooth)과 유사한 간단한 경로 부드럽게 하는 모듈을 작성하는 방법을 보여드리겠습니다.
New Script
프로젝트 내에 새 C# 스크립트를 만들고, 이름을 ModifierTutorial로 지정합니다. 좋아하는 스크립트 편집기에서 열면 다음과 같은 기본 클래스가 있습니다:
using UnityEngine;
using System.Collections;
public class ModifierTutorial : MonoBehaviour {
// 초기화에 사용
void Start () {
}
// 매 프레임 호출
void Update () {
}
}
이제 Seeker가 있는 모든 GameObject에 부착할 수 있는 modifier를 만들 것입니다. 이를 위해 MonoModifier 클래스를 상속받아야 합니다.
이 클래스는 Seeker와 Modifier 간의 기본 통신을 처리하며, modifier 작성에 큰 도움이 됩니다.
추상 클래스이므로 컴파일러 오류를 피하기 위해 일부 함수를 구현해야 합니다:
using UnityEngine;
using System.Collections;
using Pathfinding;
public class ModifierTutorial : MonoModifier {
public override int Order { get { return 60; } }
public override void Apply (Path p) {
}
}
여기까지 작성한 것은 가장 기본적인 modifier로, 실제로 아무것도 하지 않지만, 나중에 modifier를 작성할 때 템플릿으로 사용할 수 있습니다.
MonoModifier는 Pathfinding 네임스페이스에 존재하므로 "using Pathfinding" 구문을 추가했습니다.
Order 속성은 다른 modifier와의 관계에서 이 modifier가 호출될 시점을 결정합니다. 값이 클수록 나중에 호출됩니다. Order가 10인 modifier와 20인 modifier가 있으면, 10인 modifier가 먼저 호출됩니다. 내장된 modifier는 0에서 50 사이의 값을 사용하므로, 이 modifier의 order를 60으로 설정하면 내장된 모든 modifier 이후에 실행됩니다(있는 경우).
Apply 함수는 코드가 들어갈 곳이며, 경로가 후처리될 때 호출됩니다. 제공된 Path 객체는 후처리할 경로입니다.
Smoothing
사용할 부드럽게 하는 알고리즘은 매우 간단합니다. 각 포인트를 일정량씩 서로 가깝게 그립니다. Pathfinding.Path.vectorPath 배열에서 작업할 것이며, Pathfinding.Path.path 배열은 변경하지 않아야 합니다. 그렇지 않으면 다른 modifier가 깨질 수 있습니다. 먼저 경로가 성공했는지와 vectorPath 배열이 null이 아닌지 확인해야 합니다. 그렇지 않으면 NullReferenceException이 발생할 수 있습니다. 또한 경로가 단일 세그먼트로만 구성된 경우(즉, 포인트가 2개 이하인 경우) 부드럽게 할 수 없으므로 이 경우도 건너뜁니다.
public override void Apply (Path path) {
if (path.error || path.vectorPath == null || path.vectorPath.Count <= 2) {
return;
}
이제 실제 부드럽게 하기 알고리즘을 작성합니다: 경로를 더 작은 세그먼트로 나누고, 경로 배열을 루프하여 첫 번째와 마지막 포인트를 제외한 각 포인트를 인접한 포인트에 가깝게 이동시킵니다. 이 작업을 몇 번 반복하여 경로가 충분히 부드러워질 때까지 진행합니다.
public int iterations = 5;
public int subdivisions = 2;
public override void Apply (Path path) {
if (path.error || path.vectorPath == null || path.vectorPath.Count <= 2) {
return;
}
// 분할 횟수는 0보다 작을 수 없습니다.
subdivisions = Mathf.Max(subdivisions, 0);
// 잘못된 값을 입력하지 않도록 합니다.
if (subdivisions > 12) {
Debug.LogWarning("경로를 12번 이상 분할하는 것은 많습니다. 메모리 문제가 발생할 수 있으며 게임이 느려질 수 있습니다.\n" +
"이 메시지가 기록되면 부드럽게 처리가 적용되지 않습니다.");
subdivisions = 12;
return;
}
// 부드럽게 처리된 경로를 저장할 새 리스트 생성
List<Vector3> newPath = new List<Vector3>();
List<Vector3> originalPath = path.vectorPath;
// 원래 배열의 한 세그먼트(선)는 더 작은 세그먼트로 나누어집니다.
int subSegments = (int)Mathf.Pow(2, subdivisions);
float fractionPerSegment = 1F / subSegments;
for (int i = 0; i < originalPath.Count - 1; i++) {
for (int j = 0; j < subSegments; j++) {
// Vector3.Lerp를 사용하여 포인트를 선을 따라 올바른 위치에 배치합니다.
newPath.Add(Vector3.Lerp(originalPath[i], originalPath[i+1], j*fractionPerSegment));
}
}
// 마지막 포인트 추가
newPath.Add(originalPath[originalPath.Count-1]);
// [iterations] 횟수만큼 경로를 부드럽게 합니다.
for (int it = 0; it < iterations; it++) {
// 첫 번째와 마지막 포인트를 제외한 모든 포인트를 루프합니다.
for (int i = 1; i < newPath.Count-1; i++) {
// 현재 포인트와 인접한 두 포인트의 평균으로 새로운 포인트 설정
Vector3 newpoint = (newPath[i] + newPath[i-1] + newPath[i+1]) / 3F;
newPath[i] = newpoint;
}
}
// 새 경로를 p.vectorPath 필드에 할당합니다.
path.vectorPath = newPath;
}
새 경로가 p.vectorPath 필드에 할당되므로 다른 스크립트에서 이를 찾을 수 있습니다.
Pooling
메모리 효율성을 높이기 위해 리스트를 풀링하여 가비지 컬렉터의 작업을 줄일 수 있습니다.
// 풀에서 빈 리스트 가져오기
List<Vector3> newPath = Pathfinding.Util.ListPool<Vector3>.Claim();
List<Vector3> originalPath = p.vectorPath;
...
// 새 경로를 p.vectorPath 필드에 할당합니다.
p.vectorPath = newPath;
// 이전 경로는 더 이상 필요하지 않으므로 풀에 반환합니다.
Pathfinding.Util.ListPool<Vector3>.Release(originalPath);
Pooling 참조 |
Conclusion
이 튜토리얼의 끝입니다. 이 튜토리얼이 경로 modifier 작성에 도움이 되기를 바랍니다. 전체 스크립트는 여기에서 확인할 수 있습니다: ModifierTutorial.cs
'유니티 에셋 > A* Pathfinding project pro' 카테고리의 다른 글
Spherical Worlds (0) | 2024.05.28 |
---|---|
Editing graph connections manually (0) | 2024.05.28 |
Extending The System > Writing Graph Generators (0) | 2024.05.28 |
Extending The System (0) | 2024.05.28 |
Creating graphs during runtime (0) | 2024.05.28 |