본문 바로가기

[내배캠] 본 캠프 개발 학습/Basic class

11월 4일 월요일 Basic class | 코드 분석하기 (1)

📌

조금 더 꼼꼼하게 분석하기 위해서 블로그로 정리해보려고 한다. 

학습 내용 

 

1. AI 네비게이션 
2. 적 AI 코드 분석하기 


1. AI 네비게이션

 

2. 적 AI 

 

 기본적으로 몬스터 상태는 정지, 배회, 공격, 도망으로 구분된다. 플레이어와의 거리를 매 프레임마다 체크하면서 대기 상태(정지, 배회)에서 공격 상태로 전환되게 된다.  참고로 기능 중심으로 분석하기 위해서 애니메이션 코드는 정리하지 않을 예정이다.

public enum AIState
{
    Idle, // 정지
    Wandering, // 배회
    Attacking, // 공격
    Fleeing // 
}

 

 먼저 enum 으로 AI 로직에 쓸 상태를 저장한다. 

  private void Start() 
    {
        SetState(AIState.Wandering);
    }

    private void Update()
    {
        playerDistance = Vector3.Distance(transform.position, CharacterManager.Instance.Player.transform.position);

 

    Start 메서드에서 AI 상태를 Wandering 으로 설정해, 게임이 시작되면 Wandering 상태가 되도록 한다. 
   Update 메서드에서 해당 스크립트가 붙은 객체와 플레이어 객체 사이의 거리를 playerDistance로 받아온다.

 

 Q. Vector3.Distance는 어떤 기능? 

float distance = Vector3.Distance(pointA, pointB);


 A. pointA와 pointB는 두 점의 좌표를 나타내는 Vector3 객체로, 반환값은 두 점 사이의 거리(부동 소수점 값).

    private void Update()
    {
        playerDistance = Vector3.Distance(transform.position, CharacterManager.Instance.Player.transform.position);

        switch (aiState)
        {
            case AIState.Idle:
                PassiveUpdate();
                break;
            case AIState.Wandering: 
                PassiveUpdate(); 
                break;
            case AIState.Attacking: 
                AttackingUpdate(); 
                break;
            case AIState.Fleeing: 
                FleeingUpdate(); 
                break;
        }
    }

 

AIState의 각 상태에서 사용할 함수를 정리한다. 

   void PassiveUpdate()
    {
        if(aiState == AIState.Wandering && agent.remainingDistance < 0.1f)
        {
            SetState(AIState.Idle);
            Invoke("WanderToNewLocation", Random.Range(minWanderWaitTime, maxWanderWaitTime));
        }

        if(playerDistance < detectDistance)
        {
            SetState(AIState.Attacking);
        }
    }
    
        void WanderToNewLocation()
    {
        if(aiState != AIState.Idle)
        {
            return;
        }
        SetState(AIState.Wandering);
        agent.SetDestination(GetWanderLocation());
    }
    
     Vector3 GetWanderLocation()
    {
        NavMeshHit hit;

        NavMesh.SamplePosition(transform.position + (Random.onUnitSphere * Random.Range(minWanderDistance, maxWanderDistance)), out hit, maxWanderDistance, NavMesh.AllAreas);

        int i = 0;
        while (Vector3.Distance(transform.position, hit.position) < detectDistance)
        {
            NavMesh.SamplePosition(transform.position + (Random.onUnitSphere * Random.Range(minWanderDistance, maxWanderDistance)), out hit, maxWanderDistance, NavMesh.AllAreas);
            i++;
            if (i == 30)
                break;
        }

        return hit.position;
    }

 

 초기 상태가 Wandering 로 초기화 되기 때문에 매 프레임마다 PassiveUpdate를 체크하게 된다. 

 [1] PassiveUpdate 함수: remainingDistance가 0.1f보다 작을 때 Idle상태가 되며, WanderToNewLocation 함수를 실행한다. 이때 remainingDistance은 유니티의 내장 기능으로 NavMeshAgent에 지정된 목적지까지 남은 거리를 반환값으로 가진다. 해당 함수에서는 목적지까지 남은 거리가 0.1f보다 작을 때를 체크하고 있기 때문에 목적지에 도착했을 때 Idle로 전환되고 Random.Range 을 통해 랜덤 위치값을 정한다. 
 만약 playerDistance와의 거리가 detectDistance보다 작으면 Attacking 상태로 전환한다. detectDistance는 임의로 지정하는 변수로, 얼마나 가까이에 있을 때 공격 상태로 전환할 수 있는지 조절이 가능하다. 

 

[2] WanderToNewLocation 함수: 다시  Wandering 상태가 되며, GetWanderLocation 의 값을 받아서 새로운 목적지를 설정한다. 


 

 갈수록 유니티 내장 함수나 기능이 많아지면서 정보의 부족함을 느낀다. NavMeshAgent처럼 새로운 기능을 추가할 때 관련 내장 기능을 같이 알아보는 게 중요한 것 같다.