[유니티 AI] 행동: 영리한 움직임(3) - 도착하기와 떠나기

2 분 소요 No newline at end of file

도착하기와 떠나기

도착하기(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;

    }

}

뒷부분은 동일하다.


이러한 행위를 그림으로 표현하면 다음과 같다.

그림1

생각보다 복잡하지 않으면서 반경을 사용해 효과적으로 행위를 나타내고 있다. 자체만으로도 써먹을 데가 많고, 같은 원리를 응용하기도 좋아보인다.

태그: ,

카테고리: ,

업데이트:

댓글남기기