[유니티 AI] 행동: 영리한 움직임(2) - 추적과 회피

2 분 소요 No newline at end of file

추적과 회피

기본적인 행위 탬플릿을 만들었으니, 본격적으로 행위들을 만들어볼 차례다. 우선적으로 만들 행위는 추적회피다.

우선 앞서 만들었던 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()함수는 AgentBehaviourUpdate(), 즉 매 프레임 마다 정의한 데이터 형식인 SteeringAgent에 전달하여 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를 정의해준다. 여기서 미리 설정된 타겟 오브젝트는 그 정보만을 targetAuxtargetAgent에 복사하고, 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와 동일하다.


PursueEvade는 사실상 같은 클래스인데, 내부에 가지고 있는 target(실제 타겟은 아니고 예측에 사용되는 타겟)을 사용하여 다음에 어디를 갈지를 예측하고 이를 각각 SeekFlee에 전달하여 추적하거나, 도망친다.


일단 내용을 해석해서 적긴했는데 책에 설명이 너무 부족하다. 좀더 진행하면서 이해가 되면 수정하도록 하자.

작동하나 체크해보려고 유니티 에디터에서 적당한 오브젝트에 각각 컴포넌트를 붙여봤더니 한 놈은 잘 도망가고 한놈은 잘 따라오긴 한다….ㅋㅋㅋㅋ
그런데 다음 장에서 배울 것들이 이번에 만든 것들의 상위호환 느낌이라 그냥 이런 원시적인 방식이 있다 정도로 이해하고 넘어가면 될 것같다.

태그: ,

카테고리: ,

업데이트:

댓글남기기