[유니티 AI] 행동: 영리한 움직임(2) - 추적과 회피
추적과 회피
기본적인 행위 탬플릿을 만들었으니, 본격적으로 행위들을 만들어볼 차례다. 우선적으로 만들 행위는 추적과 회피다.
우선 앞서 만들었던 AgentBehaviour
을 상속하여 기본이 되는 Seek
(찾다)과 flee
(피하다) 클래스를 만들고, 이 둘을 각각 상속하는 Pursue
(추적)와 Evade
(회피) 클래스를 만들 것이다.
Seek
using UnityEngine;
public class Seek : AgentBehaviour {
public override Steering GetSteering()
{
Steering steering = new Steering();
steering.linear = target.transform.position - transform.position;
steering.linear.Normalize(); //방향벡터 설정
steering.linear = steering.linear * agent.maxAccel; //최대속도로 추적
return steering;
}
}
Seek
클래스에서는 AgentBehaviour
에서 정의했었던 GetSteering()
함수를 재정의한다. 앞서 정의 했던 GetSteering()
함수는 AgentBehaviour
의 Update()
, 즉 매 프레임 마다 정의한 데이터 형식인 Steering
을 Agent
에 전달하여 Agent
가 값에 따라 움직일 수 있게 한다.
위의 Seek
에서는 타겟의 위치에 자신의 위치를 뺀 후 정규화하여 타겟으로 향하는 방향을 구한 후, agent
의 최대속돌르 곱하여 agent
가 타겟 방향으로 최대 속도로 이동하도록 설정해준다.
Flee
using UnityEngine;
public class Flee : AgentBehaviour {
public override Steering GetSteering()
{
Steering steering = new Steering();
steering.linear = transform.position - target.transform.position;
steering.linear.Normalize(); //타겟 반대방향 설정
steering.linear = steering.linear * agent.maxAccel; //최대 속도로 도주설정
return steering;
}
}
Flee
클래스는 Seek
과 같되, 방향을 타겟의 반대방향으로 설정하는 것이 차이이다. 즉 타겟의 반대방향으로 도주하도록 설정해준다.
이렇게 정의한 두 클래스를 상속하여 보다 구체적인 행위를 작성한다.
Purse
using UnityEngine;
public class Pursue : Seek {
//Seek를 상속하는 추적 알고리즘
public float maxPrediction; //최대 예측 이동거리
GameObject targetAux;
Agent targetAgent;
...
우선 기존에 정의되지 않았던 변수를 추가해준다. 미리 설정할 수 있는 최대 예측 이동거리인 maxPrediction
, 타겟 오브젝트를 담는 targetAux
(보조타겟), 그리고 타겟의 Agnet
컴포넌트를 담는 targetAgent
, 이렇게 3가지다.
...
public override void Awake() //초기화
{
base.Awake();
targetAgent = target.GetComponent<Agent>();
targetAux = target;
target = new GameObject();
}
private void OnDestroy()
{
Destroy(targetAux);
}
...
그 후, 각각의 변수에 적절한 객체를 넣어주거나 초기화해주는 Awake
를 재정의하고, 자신이 파괴되었을 때 내부 객체를 정리할 수 있도록 OnDestroy
를 정의해준다. 여기서 미리 설정된 타겟 오브젝트는 그 정보만을 targetAux
와 targetAgent
에 복사하고, target
자체는 새로운 오브젝트를 생성하여 사용한다.
정확히는 잘 모르겠으나, targetAux
는 실제 타겟 오브젝트가 위치한 것이고, target
은 그 위치만을 표현한 것이 아닌가 싶다.
...
public override Steering GetSteering()
{
Vector3 direction = targetAux.transform.position - transform.position;
float distance = direction.magnitude; // 거리
float speed = agent.velocity.magnitude; //속도
float prediction;
//예측값 계산
if (speed <= distance / maxPrediction)
prediction = maxPrediction;
else
prediction = distance / speed;
target.transform.position = targetAux.transform.position;
target.transform.position += targetAgent.velocity * prediction;
return base.GetSteering();
}
}
마지막으로 GetSteering
을 재정의해준다.
우선 targetAux
(실제 타겟 오브젝트)와 자신과의 벡터를 뺀 후 제곱근을 구하여 거리를 측정한다. 그 후 자신의 속도인 velocity
의 절대값을 구해서 자신의 속력, speed
를 구한다.(속도는 방향값을 포함하는 벡터값이고, 속력은 오로지 힘만을 가지는 스칼라 값이다.)
그후 이동할 것으로 보이는 예측거리를 거리/최대 예측 거리로 계산한다. 만일 거리/최대 예측 거리
보다 속력이 작다면 최대 예측 거리를 예측거리로 삼는다.
이후, 타겟의 위치를 실제 타겟 오브젝트의 위치로 설정한 후, 예측거리와 타겟agent
의 속도를 곱한 만큼 타겟을 이동시켜준다. 즉 다음 프레임에서 타겟이 이동할 거리를 예측하고, 이를 목표로 삼을 위치로 삼는 것이다.(아직 이해가 잘 안됨…책에서 설명이 너무 미약해서 일단 내가 이해한 대로 적고있다.)
마지막으로 앞서 Seek
에서 정의한 GetSteering()
을 실행하여 예측한 타겟으로의 움직임을 전달한다.
Evade
Seek
대신에 Flee
를 상속하는 것 외에는 Pursue
와 동일하다.
Pursue
와 Evade
는 사실상 같은 클래스인데, 내부에 가지고 있는 target
(실제 타겟은 아니고 예측에 사용되는 타겟)을 사용하여 다음에 어디를 갈지를 예측하고 이를 각각 Seek
과 Flee
에 전달하여 추적하거나, 도망친다.
일단 내용을 해석해서 적긴했는데 책에 설명이 너무 부족하다. 좀더 진행하면서 이해가 되면 수정하도록 하자.
작동하나 체크해보려고 유니티 에디터에서 적당한 오브젝트에 각각 컴포넌트를 붙여봤더니 한 놈은 잘 도망가고 한놈은 잘 따라오긴 한다….ㅋㅋㅋㅋ
그런데 다음 장에서 배울 것들이 이번에 만든 것들의 상위호환 느낌이라 그냥 이런 원시적인 방식이 있다 정도로 이해하고 넘어가면 될 것같다.
댓글남기기