[유니티 AI] 행동: 영리한 움직임(3) - 도착하기와 떠나기
도착하기와 떠나기
도착하기(Arrive)와 떠나기(Leave)는 특정 반경을 지정해서 해당 반경에 속하게 되면 빠르게 탈출하거나 느리게 목적지에 도착하는 알고리즘이다. 기본적으로 앞서 살펴봤던 추적/회피의 기능은 사실상 다 가지고 있는데다가 훨씬 자연스러워서 이동 알고리즘은 아마 이걸 주로 쓰게 되지 않을까 싶다.
Arrive
using UnityEngine;
public class Arrive : AgentBehaviour {
public float targetRadius;
public float slowRadius;
public float timeToTarget = 0.1f;
...
우선 멤버변수를 선언해준다. targetRadius
는 도착을 판단하는 반경으로, 이 지점에 도달하면 멈추게 된다.
slowRadius
는 속도가 느려지는 반경으로, 목표에 가까워진 만큼 속도가 줄어들게 된다.
timeToTarget
은 속도를 조절해주는 변수인듯 한데 정확히는 모르겠다.
...
public override Steering GetSteering()
{
var steering = new Steering();
Vector3 direction = target.transform.position - transform.position;
float distance = direction.magnitude;
float targetSpeed;
if (distance < targetRadius)
return steering;
if (distance > slowRadius)
targetSpeed = agent.maxSpeed;
else
targetSpeed = agent.maxSpeed * distance / slowRadius;
...
GetSteering
은 두 부분으로 나누어지는데, 후술한 뒷부분은 Leave
에서도 공통으로 쓴다.
우선 두 위치를 빼서 방향과 거리를 구한 후, 이를 이용해서 이동 속도를 결정한다. 만약 distance < targetRadius
, 즉 타겟 반경에 도달했다면 이동을 멈춘다. 만약 distance > slowRadius
느려지는 반경 밖에 있다면 속도를 최대로 설정하고, 안에 있다면 거리에 반경을 나눈 값 만큼(거리가 길 수록 속도가 빠름)을 곱해서 속력를 지정해준다.
...
Vector3 desiredVelocity = direction;
desiredVelocity *= targetSpeed;
steering.linear = desiredVelocity - agent.velocity;
steering.linear /= timeToTarget;
if(steering.linear.magnitude > agent.maxAccel)
{
steering.linear.Normalize();
steering.linear *= agent.maxAccel;
}
return steering;
}
}
앞에서 구한 값들을 토대로 속도를 결정한다. 타겟과 자신과의 위치 차 벡터인 direction
을 정규화하여 방향만을 남긴 후 여기에 앞서 구한 속력, targetSpeed
를 곱해줘서 원하는 속도인 desiredVelocity
를 구한다. 그 후, 현재 자신의 속도를 빼서 필요한 속도를 만들어준다. 마지막으로 여기에 시간을 조절할 수 있도록 tiemtToTarget
을 나눠줘서 최종 속도를 구한다.
만약 이렇게 구한 최종 속도가 최대 가속치보다 크다면, 다시 정규화해서 방향만을 남긴 후, 최대 가속치로 만들어준다.(최대 속도를 넘을 수 없으므로)
Leave
using UnityEngine;
public class Leave : AgentBehaviour {
public float escapeRadius;
public float dangerRadius;
public float timeToTarget = 0.1f;
...
Leave
에선 반대로 탈출 반경과 위험 반경을 지정해준다. 탈출 반경은 즉시 최대 속도로 탈출해야하는 반경이고, 위험 반경은 점차 속도를 높혀서 탈출해야하는 반경이다.
...
public override Steering GetSteering()
{
var steering = new Steering();
Vector3 direction = transform.position - target.transform.position;
float distance = direction.magnitude;
if (distance > dangerRadius)
return steering;
float reduce;
if (distance < escapeRadius)
reduce = 0;
else
reduce = distance / dangerRadius * agent.maxSpeed;
float targetSpeed = agent.maxSpeed - reduce;
...
앞에서 타겟의 위치에서 자신의 위치를 뺐다면 여기선 반대로 하여 타겟에서 멀어지는 방향을 구한다.
만일 위험반경 밖에 있다면 움직임을 멈추고, 안에 있다면 거리에 위험 반경을 나눈 값 만큼을 빼준다.(거리가 가까울 수록 빠르다.) 탈출반경 안에 있다면 최대 속도로 탈출한다.
...
Vector3 desiredVelocity = direction;
desiredVelocity *= targetSpeed;
steering.linear = desiredVelocity - agent.velocity;
steering.linear /= timeToTarget;
if (steering.linear.magnitude > agent.maxAccel)
{
steering.linear.Normalize();
steering.linear *= agent.maxAccel;
}
return steering;
}
}
뒷부분은 동일하다.
이러한 행위를 그림으로 표현하면 다음과 같다.
생각보다 복잡하지 않으면서 반경을 사용해 효과적으로 행위를 나타내고 있다. 자체만으로도 써먹을 데가 많고, 같은 원리를 응용하기도 좋아보인다.
댓글남기기