MoveAgent

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class MoveAgent : MonoBehaviour
{
    //순찰 지점들을 저장하기 위한 List 타입 변수
    public List<Transform> wayPoints;

    //다음 순찰 지점의 배열 Index
    public int nextIdx = 0;

    //NavMeshAgent 컴포넌트를 저장할 변수
    private NavMeshAgent agent;
    private Transform enemyTr;

    private readonly float patrollSpeed = 1.5f;
    private readonly float traceSpeed = 4.0f;
    private float damping = 1.0f;
    // 순찰 여부를 판단하는 변수
    private bool _patrolling;

    public bool patrolling{
        get{return _patrolling; }
        set{
            _patrolling = value;

            if(_patrolling){
                agent.speed = patrollSpeed;
                // 순찰 상태의 회전계수
                damping = 1.0f;
                MoveWayPoint();
            }
        }
    }

    // 추적 대상의 위치를 저장하는 변수

    private Vector3 _traceTarget;

    public Vector3 traceTarget{
        get {return _traceTarget;}
        set{
            _traceTarget = value;
            agent.speed = traceSpeed;
            // 추적상태의 회전계수
            damping = 7.0f;
            TraceTarget(_traceTarget);
        }
    }

    public float speed{
        get{return agent.velocity.magnitude;}
        
    }

    void TraceTarget(Vector3 pos){
        if(agent.isPathStale) return;

        agent.destination = pos;
        agent.isStopped = false;
        Debug.Log("??");

    }

    // Start is called before the first frame update
    void Start()
    {
        enemyTr = GetComponent<Transform>();
        agent = GetComponent<NavMeshAgent>();
        agent.autoBraking = false;
        // 자동으로 회전하는 기능 비활성화\\
        agent.updatePosition = false;
        agent.speed = patrollSpeed;
        var group = GameObject.Find("WayPointGroup");
       
        if(group != null)
        {
            group.GetComponentsInChildren<Transform>(wayPoints);
            wayPoints.RemoveAt(0);
        }
        MoveWayPoint();
    }

    private void MoveWayPoint()
    {
        //최단거리 경로 계산이 끝나지 않았으면 다음을 수행하지 않음
        if (agent.isPathStale) return;

        //다음 목적지를 wayPoints 배열에서 추출한 위치로 지정
        agent.destination = wayPoints[nextIdx].position;
        agent.isStopped = false;

    }

    public void Stop(){
        agent.isStopped = true;
        // 바로 정지하기 위해 속도를 0으로 설정
        agent.velocity = Vector3.zero;
        _patrolling = false;

    }

    // Update is called once per frame
    void Update()
    {   
    
        if(agent.isStopped == false){
            
            // NavMeshAgent가 가야할 방향 벡터를 쿼터니언 타입의 각도로 변환
            Quaternion rot = Quaternion.LookRotation(agent.desiredVelocity);
            // 보간 함수를 사용해 점진적으로 회전시킴
            enemyTr.rotation = Quaternion.Slerp(enemyTr.rotation, rot, Time.deltaTime*damping);
        }

        if(!_patrolling) return;

        //NavMeshAgent가 이동하고 있고 목적지에 도착했는지 여부를 계산
        if(agent.velocity.sqrMagnitude >= 0.2f * 0.2f && agent.remainingDistance <= 0.5f)
        {

            //다음 목적지의 배열 첨자를 계산
            nextIdx = ++nextIdx % wayPoints.Count;
            //다음 목적지로 이동 명령을 수행
            MoveWayPoint();
        }
    }
}

EnemyAi

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyAI : MonoBehaviour
{   

    public enum State{
        PATROL,
        TRACE,
        ATTACK,
        DIE
    }

    public State state = State.PATROL;

    private Transform playerTr;
    private Transform enemyTr;

    // Animator 컴포넌트 저장할 변수
    private Animator animator;

    public float attackDist = 5.0f;
    public float traceDist = 10.0f;

    public  bool isDie = false;

    private WaitForSeconds ws;

    // 이동을 제어하는 MoveAgent클래스를 저장할 변수
    private MoveAgent moveAgent;

    // 애니메이터 컨트롤러에 정의한 파라미터의 해시값을 미리 추출

    private readonly int hashMove = Animator.StringToHash("IsMove");
    private readonly int hashSpeed = Animator.StringToHash("Speed");

    private void Awake(){
        var player = GameObject.FindGameObjectWithTag("Player");

        if(player !=null){
            playerTr = player.GetComponent<Transform>();
        }

        enemyTr = GetComponent<Transform>();
        moveAgent = GetComponent<MoveAgent>();
        animator = GetComponent<Animator>();

        ws = new WaitForSeconds(0.3f);

    }

    private void OnEnable(){
        StartCoroutine(CheckState());
        StartCoroutine(Action());
    }

    IEnumerator Action(){

        // 적 캐릭터가 사망할 때 까지 무한루프

        while (!isDie){
            yield return ws;

            switch(state){
                case State.PATROL:
                    moveAgent.patrolling = true;
                    animator.SetBool(hashMove, true);
                    break;

                case State.TRACE:
                    moveAgent.traceTarget = playerTr.position;
                    animator.SetBool(hashMove, true);
                    break;

                case State.ATTACK:
                    moveAgent.Stop();
                    animator.SetBool(hashMove, false);
                    break;

                case State.DIE:
                    moveAgent.Stop();
                    break;
            }
        }
    }

    IEnumerator CheckState(){
        while(!isDie){
            if(state == State.DIE) yield break;

            float dist = Vector3.Distance(playerTr.position, enemyTr.position);

            if(dist <= attackDist){
                state = State.ATTACK;
            }
            else if (dist <= traceDist){
                state = State.TRACE;
            }
            else{
                state = State.PATROL;
            }
            yield return ws;
        }
    }

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        animator.SetFloat(hashSpeed, moveAgent.speed);
    }
}