본문 바로가기
개발/Unity) 코드분석

코드분석) blog.gamedev.tv -Unity의 AI 인식 예제 프로젝트

by 테샤르 2022. 1. 7.

blog.gamedev.tv -Unity의 AI 인식 예제 프로젝트

Unity의 기능 중  NavMesh로 인해서 길을 찾고 FSM을 이용한 상태 패턴으로 간단한 AI를 만드는 기본적인 예제의 코드와 구현 방식에 대한 코드 리뷰입니다.

 

원본 URL : [링크]

 

How to make AI sentient in Unity, Part I

In the first of two parts miniseries about AI senses, I'm going to guide you through an implementation of eyes and ears for NPCs, that takes advantage of inheritance and UnityEvents. In the subsequent part, I'll show you how to create some reactive behavio

blog.gamedev.tv

프로젝트 링크 주소 : [링크]

 

GitHub - marianpekar/ai-senses-unity-example: An example project for series of blog posts about AI perception in Unity published

An example project for series of blog posts about AI perception in Unity published at blog.gamedev.tv - GitHub - marianpekar/ai-senses-unity-example: An example project for series of blog posts abo...

github.com

 

<플레이 영상>

 

 

------------------------------------

Enemy Object와 Player 2가지와 기본적인 환경 파일로 Scene은 구성되어있다.

Enemy 에는 NavMeshAgent와 AlController, Patrol , Chase,  의 스크립트의 컴포넌트가 있고 

Eyes와 Ears를 통해서 Player를 감지하도록 구성되어 있다.

 

DrawGizmos를 통해서 Eyes의 영역과 Ears의 영역을 확인할 수 있도록 구성되어있다.

<Sense>

public class Sense : MonoBehaviour
{
    public Detectable Detectable;
    public float Distance;

    protected bool IsSensing;

    public bool IsDetectionContinuous = true;

    public UnityAction<Detectable> OnDetect;
    public UnityAction<Detectable> OnLost;

#if UNITY_EDITOR
    public Color DebugDrawColor = Color.green;
#endif

    private void Detect(Detectable detectable) 
    {
        IsSensing = true;
        OnDetect?.Invoke(detectable);
    }
    private void Lost(Detectable detectable)
    {
        IsSensing = false;
        OnLost?.Invoke(detectable); 
    }

    void Update()
    {
        if (IsSensing)
        {
            if (!HasDetected(Detectable))
            {
                Lost(Detectable);
                return;
            }

            if(IsDetectionContinuous) 
            {
                Detect(Detectable);
            }
        }
        else
        {
            if (!HasDetected(Detectable))
                return;

            Detect(Detectable);
        }      
    }

    protected virtual bool HasDetected(Detectable detectable) => false;
}

<Ears>

public class Ears : Sense
{
    protected override bool HasDetected(Detectable detectable)
    {
        return Vector3.Distance(detectable.transform.position, transform.position) <= Distance && detectable.CanBeHear;
    }

#if UNITY_EDITOR
    private void OnDrawGizmos()
    {
        Gizmos.color = DebugDrawColor;
        Gizmos.DrawWireSphere(transform.position, Distance);
    }
#endif
}

 

<AIController>

public class AIController : MonoBehaviour
{
    private AIBehaviour currentBehavior;
    public AIBehaviour CurrentBehavior
    {
        get => currentBehavior;
        private set
        {
            currentBehavior?.Deactivate(this);
            value.Activate(this);
            currentBehavior = value;
        }
    }

    private Patrol PatrolBehavior;
    private Investigate InvestigateBehavior;
    private Chase ChaseBehavior;

    private NavMeshAgent agent;
    public float RemainingDistance { get => agent.remainingDistance; }
    public float StoppingDistance { get => agent.stoppingDistance; }
    public void SetDestination(Vector3 destination) => agent.SetDestination(destination);

    private float defaultAgentSpeed;
    public void MultiplySpeed(float factor) => agent.speed = defaultAgentSpeed * factor;
    public void SetDefaultSpeed() => agent.speed = defaultAgentSpeed;

    private Eyes eyes;
    private Ears ears;
    public void IgnoreEars(bool ignore) => ears.gameObject.SetActive(!ignore);

    void Start()
    {
        agent = GetComponent<NavMeshAgent>();
        defaultAgentSpeed = agent.speed;

        PatrolBehavior = GetComponent<Patrol>();
        InvestigateBehavior = GetComponent<Investigate>();
        ChaseBehavior = GetComponent<Chase>();

        eyes = GetComponentInChildren<Eyes>();
        eyes.OnDetect += Chase;
        eyes.OnLost += Investigate;

        ears = GetComponentInChildren<Ears>();
        ears.OnDetect += Investigate;

        Patrol();
    }

    void Update()
    {
        CurrentBehavior.UpdateStep(this);
    }

    public void Patrol()
    {
        CurrentBehavior = PatrolBehavior;
    }

    public void Investigate(Detectable detectable) 
    {
        InvestigateBehavior.Destination = detectable.transform.position;
        CurrentBehavior = InvestigateBehavior;
    }

    public void Chase(Detectable detectable) 
    {
        ChaseBehavior.Target = detectable.transform;
        CurrentBehavior = ChaseBehavior;
    }
}

 

 

<Patrol Point>

<Patrol>

처음 시작할때 기본적으로 Patrol의 정해진 Point를 기준으로 이동을 반복합니다.

 

public class Patrol : AIBehaviour
{
    public Transform[] PatrolPoints;
    private int currentPPIndex;

    public override void Activate(AIController controller)
    {
        controller.SetDefaultSpeed();
        controller.SetDestination(PatrolPoints[currentPPIndex].position);    
    }

    public override void UpdateStep(AIController controller)
    {
        if (controller.RemainingDistance <= controller.StoppingDistance) {
            currentPPIndex = currentPPIndex < PatrolPoints.Length - 1 ? currentPPIndex + 1 : 0;
            controller.SetDestination(PatrolPoints[currentPPIndex].position);
        }
    }
}

Sense를 통해서 탐지할 항목이 탐지되면 다음 행동으로 넘어갑니다.

 

Patrol -> Chase -> Investigate의 형태로 State가 변경된다.

 

-----------------------------------

총평 

<귀 구성요소>

 

<눈 구성요소>

 

블로그에서도 말하는것처럼 단순하게 Rule Base System으로 구성되어서 기본적인 AI 구성에는 도움 될만한 코드라고 생각이 든다. NavMesh를 통한 길 찾기, FSM, 탐지 요소 등등이 포함되어있다.

 

[Unity -Top Paid Package]

[Unity -Top Free Package]

[Unity -New Asset Package]

 

★☆

반응형

댓글