지난 벽 피하기에서는 가중치에 의한 행위를 섞었다. 가만 가중치에 의한 행위는 가중치가 큰 행위가 가중치가 가장 작은 행위를 희미하게 만들기에 행위를 섞기에는 충분하지 못하다. 그래서 고순위부터 저순위까지 순차적으로 적용되는 우선순위 기반의 행위 섞기가 필요하다.
이건 기본 골조인 AgentBehaviour와 Agent 클래스를 변경해야한다.
AgentBehaviour.cs
// 모든 행위의 기본 클래스
public class AgentBehaviour : MonoBehaviour
{
public GameObject target; // 에이전트가 주시하고 있는 대상
protected Agent agent; // 행위를 조작할 에이전트
public float weight = 1.0f; // 행위 가중치
public int priority = 1; // 우선순위
public virtual void Awake()
{
agent = gameObject.GetComponent<Agent>();
}
public virtual void Update()
{
// 현재 에이전트의 행위를 수행합니다.
// 에이전트에 행위에 대한 우선순위도 부여합니다.
agent.SetSteering(GetSteering(), priority);
}
// 기본적으로는 아무것도 수행하지 않는다.
public virtual Steering GetSteering()
{
return new Steering();
}
// 두 방향 값을 뺀 후 실제 회전 방향을 찾는다.
public float MapToRange(float rotation)
{
// 회전값이 360을 넘어가지 않도록 합니다.
rotation %= 360.0f;
if (Mathf.Abs(rotation) > 180.0f)
// 회전값이 -180 ~ 180 으로 유지될 수 있도록합니다.
{
if (rotation < 0.0f)
rotation += 360.0f;
else
rotation -= 360.0f;
}
return rotation;
}
// 현재의 방향값을 벡터로 바꿔줍니다.
public Vector3 OriToVec(float orientation)
{
// ex) OriToVec(90) z 축 양의 방향 벡터
Vector3 vector = Vector3.zero;
vector.x = Mathf.Sin(orientation * Mathf.Deg2Rad) * 1.0f;
vector.z = Mathf.Cos(orientation * Mathf.Deg2Rad) * 1.0f;
return vector.normalized;
}
}
Agent.cs
// 행위를 수행하는 주체
public class Agent : MonoBehaviour
{
public float maxSpeed; // 최대 속도
public float maxAccel; // 최대 가속도
public float maxRotation; // 최대 회전 속도
public float maxAngularAccel; // 최대 회전 가속도
public float orientation; // 프레임 당 회전해야하는 수치
public float rotation; // 회전해야하는 양
public Vector3 velocity; // 이동 벡터
protected Steering steering; // 현재 행위
public float priorityThreshold = 0.2f; // 우선순위 임계치 입니다. 각 행위의 속도 회전 값이 최소 이 수치를 넘어야지 실행됩니다.
private Dictionary<int, List<Steering>> groups; // 행위 그룹입니다.
private void Start()
{
velocity = Vector3.zero;
steering = new Steering();
groups = new Dictionary<int, List<Steering>>();
}
// 현재 velocity와 rotation 값에 따라 이동과 회전을 수행한다
public virtual void Update()
{
Vector3 displacement = velocity * Time.deltaTime; // 프레임당 나아가야하는 벡터
orientation += rotation * Time.deltaTime; // 프레임당 회전해야하는 수치 조정
// 회전 값을 0 ~ 360도 고정
if (orientation < 0.0f)
orientation += 360.0f;
else if (orientation > 360.0f)
orientation -= 360.0f;
// 프레임당 이동하고 회전합니다.
transform.Translate(displacement, Space.World);
transform.rotation = new Quaternion();
transform.Rotate(Vector3.up, orientation);
}
// 현재 행위에 따라 다음 프레임의 움직임을 갱신합니다.
public virtual void LateUpdate()
{
steering = GetPrioritySteering(); // 우선순위를 고려한 행위 수치를 얻습니다.
groups.Clear();
// 현재 행위한테서 어느 방향으로 얼마나 이동하는지 가져옵니다.
velocity += steering.linear * Time.deltaTime;
// 현재 행위한테서 얼마나 회전해야하는지 가져옵니다.
rotation += steering.angular * Time.deltaTime;
if (velocity.magnitude > maxSpeed)
// 현재 이동 벡터의 크기가 현재 최대 속도를 넘어선다면
{
// 이동 벡터를 정규화 한 후 최대 속도 만큼 곱해줍니다.
velocity.Normalize();
velocity = velocity * maxSpeed;
}
if (steering.angular == 0.0f)
// 회전 값이 0이라면 rotation도 0
{
rotation = 0.0f;
}
if (steering.linear.sqrMagnitude == 0.0f)
// 현재 속도가 0이라면
{
// 이동 벡터도 0
velocity = Vector3.zero;
}
// 프레임 당 행위 계산을 실시하기위한 초기화
steering = new Steering();
}
// 행위를 세팅합니다.
// AgentBehaviour 클래스가 매 프레임마다 행위를 갱신합니다.
public void SetSteering(Steering steering, float weight)
{
// 현재 가중치만큼 속도와 회전 양을 결정합니다.
this.steering.linear += (weight * steering.linear);
this.steering.angular += (weight * steering.angular);
}
// 우선순위를 고려한 행위를 세팅합니다.
public void SetSteering(Steering steering, int priority)
{
if (!groups.ContainsKey(priority))
{
groups.Add(priority, new List<Steering>());
}
groups[priority].Add(steering);
}
// 우선순위에 의한 행의 수치를 얻습니다.
private Steering GetPrioritySteering()
{
Steering steering = new Steering();
// 우선순위를 위한 임계치를 설정합니다.
// sqrMagnitude 과 비교하기 위해 제곱합니다.
// sqrMagnitude 를 사용하는 이유는 magnitude 보다 연산량이 적어서..
float sqrThreshold = priorityThreshold * priorityThreshold;
foreach (List<Steering> group in groups.Values)
{
steering = new Steering();
foreach (Steering singleSteering in group)
// 같은 동일 우선순위 그룹내의 행위를 모두 연산합니다.
{
steering.linear += singleSteering.linear;
steering.angular += singleSteering.angular;
}
if (steering.linear.sqrMagnitude > sqrThreshold ||
Mathf.Abs(steering.angular) > priorityThreshold)
// 만약 그룹내의 행위의 속도 및 회전양이 최소 임계치 영역을 넘었다면
// 해당 행위를 수행합니다.
{
return steering;
}
}
return steering;
}
}
아래는 벽피하기예제를 좀더 수정했다. 파랑색 에이전트는 우선순위 순서대로 에이전트 회피, 벽피하기, 붉은색 오브젝트 추적 행위를 수행한다. 녹색 에이전트는 벽피하기, 붉은색 오브젝트 추적으로 되어있다.
파랑색 오브젝트는 속도가 녹색에이전트보다 빨라서 녹색에이전트보다 빨리 붉은색 에이전트로 향해야하지만 에이전트 회피의 우선순위가 추적 우선순위보다 높기에 녹색 오브젝트를 피해서 붉은색 에이전트로 향하는걸 볼 수 있다.

'Unity > Unity_AI' 카테고리의 다른 글
| 조작 행위_벽 피하기(AvoidWall) (0) | 2023.08.03 |
|---|---|
| 조작 행위_대상 회피(AvoidAgent) (0) | 2023.08.02 |
| 조작 행위_경로 추적하기(PathFollower) (0) | 2023.07.31 |
| 조작 행위_주변배회(Wander) (0) | 2023.07.30 |
| 조작 행위_도착(Arrive), 떠남(Leave) (0) | 2023.07.27 |