기타/미분류

유한상태기계

http://hyunity3d.tistory.com/575

using UnityEngine;
using System.Collections;
 
public class MonsterCtrl : MonoBehaviour 
{
 
    //몬스터의 상태정보 있는 eNUMERABLE 변수 선언
    public enum MonsterState { idle, trace, attack, die};
    public MonsterState monsterState = MonsterState.idle;
 
    //각종 컴포넌트 미리 저장
    private Transform monsterTr;
    private Transform playerTr;
    private NavMeshAgent nvAgent;
 
 
    //추적 거리.
    public float traceDist = 10f;
    //공격 거리.
    public float attackDist = 2f;
    //몬스터 사망 여부
    private bool isDie = false;
 
    // Use this for initialization
    void Start () 
    {
        monsterTr = GetComponent<Transform>();
        playerTr = GameObject.FindWithTag("Player").GetComponent<Transform>();
        nvAgent = GetComponent<NavMeshAgent>();
 
        //일정한 간격으로 몬스터의 행동상태를 체크
        StartCoroutine(this.CheckMonsterState());
        //몬스터의 상태에 따른 동작
        StartCoroutine(this.MonsterAction());
    }
 
    IEnumerator CheckMonsterState()
    {
        while(! isDie)
        {
            yield return new WaitForSeconds(0.2f);
 
            //몬스터와 플레이어 거리측정.
            float dist = Vector3.Distance(playerTr.position, monsterTr.position);
 
            if(dist <= attackDist)   //공격 범위 이내에 들어왔는가?
            {
                monsterState = MonsterState.attack;
            }
            else if(dist <= traceDist) //추적 범위 이내에 들어왔는가?
            {
                monsterState = MonsterState.trace;
            }
            else
            {
                monsterState = MonsterState.idle;
            }
        }
    }
 
    IEnumerator MonsterAction()
    {
        while(! isDie)
        {
            switch(monsterState)
            {
            case MonsterState.idle:
                //추적중지.
                nvAgent.Stop();
                break;
            case MonsterState.trace:
                nvAgent.destination = playerTr.position;
                break;
            case MonsterState.attack:
                break;
            }
            yield return null;
        }
    }
 
    // Update is called once per frame
    void Update () 
    {
 
    }
}

유니티를 사용할때 유한상태기계(FSM) 기법을 많이 사용한다. 유니티 자체에 제공하는 애니메이션 시스템도 유한상태기계를 사용하고 있어서 코딩을 할때도 이 기법을 사용하면 관리하기 편하다.

enum 으로 몬스터의 상태를 설정해주고 각 상태를 체크 해주며, 특정조건에 따라 상태를 변경 시켜준다. 여기선 코루틴을 사용하여 0.2초마다 몬스터의 상태를 체크해주어서 최적화를 시켜주었다.( 코루틴을 사용하지 않으면 매 프레임마다 체크를 하므로 성능이 저하될 우려가 있음.)

대강 흐름도는 이렇다.

void Start () 
{
	StartCoroutine(this.CheckMonsterState());
	StartCoroutine(this.MonsterAction());
}

CheckMonstState 에서는 0.2초 간격으로 몬스터의 상태를 체크해준다.

  • 플레이어와의 거리가 공격사정거리보다 가까우면 → 몬스터의 상태를 공격상태로 변경하고
  • 플레이어와의 거리가 추적사정거리보다 가까우면 → 몬스터의 상태를 추적상태로 변경한다.
  • 그것도 아니라면 몬스터의 상태를 idle 상태로 변경한다.

MonstAction 에서는 switch 에서 각각의 상태에 따라 추적을 중지하거나 추적을 시작한다.