상세 컨텐츠

본문 제목

유니티 유한상태기계 - FSM(Finite State Machine) 1

유니티/기능

by MJ_119 2025. 1. 25. 00:44

본문

유니티 유한상태기계 - FSM(Finite State Machine)

 

유데미 강의듣고 정리중

 

기본 구조

 

Enemy 스크립트는 근거리 적, 원거리 적, 보스 등 다양하게 파생될수 있음.

그러므로 공통적인 모든것을 가지고 있어야함.

 

- 예를들어 Enemy_Melee(근접 몬스터)는 Enemy를 상속

 

 

Enemy 스크립트의 기본 틀 :

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

public class Enemy : MonoBehaviour
{
    public EnemyStateMachine enemyStateMachine { get; private set; }

    protected virtual void Awake()
    {
        enemyStateMachine = new EnemyStateMachine();
    }

    protected virtual void Start()
    {
    
    }

    protected virtual void Update()
    {
        
    }

}

 

Idle, Move(내비메쉬) 기능 등을 추가한 Enemy 스크립트 :

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

public class Enemy : MonoBehaviour
{

    [Header("Idle data")]
    public float idleTime;
    public float aggresionRange;


    [Header("Move data")]
    public float moveSpeed;

    public Transform[] patrolPoints;
    int currentPatrolIndex;

    
    public Transform player { get; private set; }
    public NavMeshAgent agent { get; private set; }
    public EnemyStateMachine enemyStateMachine { get; private set; }

    protected virtual void Awake()
    {
        enemyStateMachine = new EnemyStateMachine();

        agent = GetComponent<NavMeshAgent>();
    }

    protected virtual void Start()
    {
        foreach (var p in patrolPoints)
        {
            p.parent = null;
        }
    }

    protected virtual void Update()
    {
        
    }

    public Vector3 GetPatrolDestination()
    {
        Vector3 destination = patrolPoints[currentPatrolIndex].transform.position;

        currentPatrolIndex++;

        if (currentPatrolIndex >= patrolPoints.Length)
            currentPatrolIndex = 0;

        return destination;
    }
    
    protected virtual void OnDrawGizmos()
    {
        Gizmos.DrawWireSphere(transform.position, aggresionRange);
    }
}

 

Enemy_Melee 스크립트 :

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

public class Enemy_Melee : Enemy
{
    public IdleState_Melee idleState { get; private set; }
    public MoveState_Melee moveState { get; private set; }

    protected override void Awake()
    {
        base.Awake(); 

        idleState = new IdleState_Melee(this, enemyStateMachine, "Idle");
        moveState = new MoveState_Melee(this, enemyStateMachine, "Move");
    }

    protected override void Start()
    {
        base.Start();

        enemyStateMachine.Initialize(idleState);
    }

    protected override void Update()
    {
        base.Update();

        enemyStateMachine.currentState.Update();
    }
}

 

 

EnemyState 스크립트 :

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

public class EnemyState
{
    protected Enemy enemyBase;
    protected EnemyStateMachine enemyStateMachine;
    protected string animBoolName;

    public EnemyState(Enemy enemyBase, EnemyStateMachine enemyStateMachine, string animBoolName)
    {
        this.enemyBase = enemyBase;
        this.enemyStateMachine = enemyStateMachine;
        this.animBoolName = animBoolName;
    }
        
    public virtual void Enter()
    {

    }

    public virtual void Update()
    {
        
    }

    public virtual void Exit()
    {

    }
}

 

 

EnemyStateMachine 스크립트 :

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

public class EnemyStateMachine
{
    public EnemyState currentState { get; private set; }

    // 첫번째 상태를 초기화함
    public void Initialize(EnemyState startState)
    {
        currentState = startState;
        currentState.Enter();
    }

    // 이전상태를 종료하고 바로 다른 상태로 전환
    public void ChangeState(EnemyState newState)
    {
        Debug.Log($"Changing state from {currentState} to {newState}");
        currentState.Exit(); // 이전 상태를 종료하고
        currentState = newState; // 새로운 상태를 가져와서
        currentState.Enter(); // 새로운 상태로 들어감
    }
}

 

 

 

IdleState_Melee 스크립트 :

 - 타이머를 추가해서 지정한 시간이 지나면 다른 상태로 전환

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

public class IdleState_Melee : EnemyState
{
    private Enemy_Melee enemy;

    public IdleState_Melee(Enemy enemyBase, EnemyStateMachine enemyStateMachine, string animBoolName) : base(enemyBase, enemyStateMachine, animBoolName)
    {
        // Enemy_Melee에 있는 다른 상태로 접근 및 전환하기 위해 선언.
        // ex) enemyStateMachine.ChangeState(enemy.moveState);
        enemy = enemyBase as Enemy_Melee;
    }

    public override void Enter()
    {
        base.Enter();

        stateTimer = enemyBase.idleTime;
    }

    public override void Exit()
    {
        base.Exit();
    }

    public override void Update()
    {
        base.Update();

        if(stateTimer < 0)
        {
            enemyStateMachine.ChangeState(enemy.moveState);
        }
    }

}

 

 

 

 

 

 

관련글 더보기