Unity Engine/Unity 3D.

[Unity 3D] Creating Monster State Machine

Muru 2023. 10. 21. 20:42
Monster의 기본적인 패턴의 변화 구상

IDLE(대기) : 멈추는 상태
Patrol(정찰) :  순찰하며 가볍게 걷는 상태, 감지 되는 거리에 오게되면 Chase상태로 전환
Chase(추적) : 추격하는 상태로 빠르게 걷는 상태, 플레이어를 추격 
Attack(공격) : Chase상태에서 공격범위 내에 들어오면 공격을 실행한다.
Evade(회피) : 필수는 아니지만, 몬스터가 쉽게 죽을수없도록 회피 기능도 구현했다.
Death(죽음) : 체력이 0이되면 몬스터는 사망하며 삭제

이정도로 구성해봤다.
using UnityEngine;

public class EnemyController : MonoBehaviour
{
    public enum State
    {
        IDLE,
        CHASE,
        PATROL,
        ATTACK,
        EVADE,
        DEATH
    }

    public State currentState;

    private void Awake()
    {
        currentState = State.CHASE; // 시작 상태 Chsse 설정
    }

    private void Update()
    {
        switch (currentState)
        {
            case State.IDLE:
                Idle();
                break;
                
            case State.PATROL:
                Patrol();
                break;
                
            case State.CHASE:
                Chase();
                break;

            case State.ATTACK:
                Attack();
                break;

            case State.EVADE:
                Evade();
                break;

            case State.DEATH:
                Death();
                break;
        }
    }

    private void Idle()
    {
        // IDLE 상태에서 수행될 로직
        Debug.Log(" 현재 상태 : " + currentState);

    }

    private void Patrol()
    {
        // Patrol 상태에서 수행될 로직
        Debug.Log(" 현재 상태 : " + currentState);
    }

    private void Chase()
    {
        // CHASE 상태에서 수행될 로직
        Debug.Log(" 현재 상태 : " + currentState);
    }

    private void Attack()
    {
        // ATTACK 상태에서 수행될 로직
        Debug.Log(" 현재 상태 : " + currentState);
    }

    private void Evade()
    {
        // Evade상태에서 수행될 로직
        Debug.Log(" 현재 상태 : " + currentState);
    }

    private void Death()
    {
        // DEATH 상태에서 수행될 로직
        Debug.Log(" 현재 상태 : " + currentState);
    }
		
}

여기까지 기본 상태완료!

이제 Idle, Patrol, Chase, Attack, Evade, Death에 넣고싶은 내용을 넣어주도록하자.

 

가볍게 움직이는 코드까지 작성해보겠습니다. 

[SerializeField] private float patrolRadius = 5f;	//순찰반경
[SerializeField] private float partolDuration = 5f;	//순찰 후 한 위치에 멈출 시간
[SerializeField] private float detectionRadius = 10f;	//적이 플레이어를 감지하는 거리
private Vector3 nextPatrolPoint;	//다음 순찰 지점
private Transform playerTransform;	//추적할 위치

...

private void Awake()
{
    currentState = State.PATROL; //몬스터 생성시 순찰
    playerTransform = GameObject.FindGameObjectWithTag("Player").transform;	//태그로 찾기
    ChooseNextPatrolPoint();
}

 

먼저 순찰반경, 순찰후 멈출 시간, 플레이어 감지 범위, 순찰지점, 플레이어를 추적할수있게 적었습니다.

그리고 곧장 다음 순찰 지점으로 이동할수있게 만들었습니다.

private void ChooseNextPatrolPoint()
{
	//공중으로는 이동 안할거니까 X,Z만
    float randomX = UnityEngine.Random.Range(-patrolRadious, patrolRadius);
    float randomZ = UnityEngine.Random.Range(-patrolRadious, patrolRadius);
    
    nextPatrolPoint = transform.position + new Vector3(randomX, 0f, randomZ);
}

 

순찰할 지점 알려주고 움직일수 있도록 아래와 같이 작성

private void Patrol()
{
    //패트롤 상태 되었는지 확인
    Debug.Log("Current State : " + currentState);
    
    //플레이어의 위치 찾기
    float distanceToPlayer = Vector3.Distance(transform.position, playerTransform.positon);
    //다음 순찰할 곳 찾기 : 바로 위에서 랜덤하게 생성했죠?
    float distanceToPatrolPoint = Vector3.Distance(transform.position, nextPatrolPoint);
    
    //플레이어가 반경내에 들어왔다면 추적상태로 변경
    if (distanceToPlayer <= detectionRadius)
    {
       currentState = State.CHASE;
       return;
    }
    
    //순찰 지점에 거의 도착했다면
    if (distanceToPatrolPoint <= 0.5f) 	//0.5는 임의의 값
    {
        currentState = State.IDLE;	//대기 상태로 전환
        StatCoroutine(StayForAWhile());  //대기하면서 뭐할지 코루틴 작성
        return;
    }
    
    //위에 해당되는 내용이 없다면 이동한다 (else)
    transform.position = Vector3.MoveTowards(transform.position, nextPatrolPoint, speed * Time.deltaTIme);
}

조건 두개를 걸고 좀전에 찾은 순찰지점을 향해 이동합니다. 조건 두개는 다음과 같습니다. 플레이어가 감지되는 거리에 왔으면 추적을위해 추적 상태로 전환하거나.

private IEnumerator StayForAWhile()
{
     Debug.Log("순찰 끝! 잠깐 쉬자..");
     
     //잠깐 쉬고, 다음 순찰 지점 찾기
     yield return new WaitForSeconds(patrolDuration);
     ChooseNextPatrolPoint();
     
     //다시 순찰 상태로 전환
     currentState = State.PATROL;
}

순찰지점 다 도착했으면 잠깐 쉬었다 다시 순찰할수있도록 코루틴을 작성.

 

추적 공격 회피 죽음 등은 나중에...