본문 바로가기
작심삼일/로그라이크 게임 개발 일지

적 패턴 구현(8)

by water_beetle 2023. 8. 2.

적이 가만히 있다가 플레이어가 가까이 오면 추적하고,

가끔씩 돌진도 만들게 싶어서 Finite State Machine을 이용해 해당 방법을 구현해 보았습니다.

 

Udemy에 있는 FSM 구현방법과, 도서관에서 빌린 "유니티로 배우는 게임 디자인패턴"에 있는
상태 패턴 코드를 읽어본 다음 저만의 방식으로 그냥 한번 구현해 보았습니다.

 

코드를 작성하기전에 미리 어떻게 구현할지 글로 한번 정리해보았습니다...

구현 방법

제가 구현한 방법은 크게 EnemyAI, EnemyStateContext, EnemyState(abstract)  세가지 클래스로

나누어서 구현하였습니다.

 

EnemyAI

매 Update마다 EnemyStateContext에 저장된 current State를 실행시킵니다.
-> _enemyStateContext.RunState() 부분

public class EnemyAI : MonoBehaviour
{
    [SerializeField]
    public Transform target;
    private EnemyStateContext _enemyStateContext;

    [Header("Events"), Space(10)]
    [SerializeField]
    private GameEventSO enemyMoveEvent;

    private void Awake()
    {
        target = GameObject.FindGameObjectWithTag("Player").transform;
        _enemyStateContext = GetComponentInChildren<EnemyStateContext>();
    }

    // Update is called once per frame
    void Update()
    {
        _enemyStateContext.RunState();
    }

    public void Transition(State state)
    {
        _enemyStateContext.Transition(state);
    }

    public void Move(AgentMovementParameter movementParameter)
    {
        enemyMoveEvent.Raise(this, movementParameter);
    }
}

EnemyStateContext

State를 관리하는 클래스 입니다.

현재 위치하고 있는 currentState 변수와,

state를 실행하는 함수, 다음 state로 이동하는 transition함수로 구성되어 있습니다.

[RequireComponent(typeof(EnemyIdleState))]
[RequireComponent(typeof(EnemyPursueState))]
public class EnemyStateContext : MonoBehaviour
{
    public EnemyState CurrentState { get; set; }


    private EnemyState _IdleState, _AttckState, _PursueState, _AvoidState, _RushState, _WanderState;

    private void AttachStatetoObject()
    {
        _IdleState = gameObject.GetComponent<EnemyIdleState>();
        _PursueState = gameObject.GetComponent<EnemyPursueState>();
        _RushState = gameObject.GetComponent<EnemyRushState>();
    }

    // Start is called before the first frame update
    void Start()
    {
        AttachStatetoObject();
        CurrentState = _IdleState;
    }

    public void Transition(State state)
    {
        switch (state)
        {
            case State.IdleState:
                CurrentState = _IdleState;
                break;
            case State.AttckState:
                CurrentState = _AttckState;
                break;
            case State.PursueState:
                CurrentState = _PursueState;
                break;
            case State.AvoidState:
                CurrentState = _AvoidState;
                break;
            case State.RushState:
                CurrentState = _RushState;
                break;
            case State.WanderState:
                CurrentState = _WanderState;
                break;
        }
    }

    public void RunState()
    {
        CurrentState.Handle();
    }
}

Enemy State

public abstract class EnemyState : MonoBehaviour
{
    protected EnemyAI _enemyAI;

    protected virtual void Awake()
    {
        _enemyAI = GetComponentInParent<EnemyAI>();

    }

    public void Handle()
    {
        Action();
        Decision();
    }

    protected abstract void Action();
    protected abstract void Decision();

}

추상 클래스로 해당 클래스를 상속받아 concrete state(Idle, Attack, Move 등등)을 만듭니다.

크게 Action과 Decision으로 이루어져 있습니다.

Action은 해당 State가 수행해야 하는 일로, Move는 움직이기, Idle은 가만히 있기를 예로 들 수 있습니다.

Decision은 해당 State가 다음 state로 가야할 조건을 판단하고, 다음 state로 이동하게 됩니다.

// StateIdle에서의 Decision 구현
protected override void Decision()
    {
        float distance = Vector3.Distance(_enemyAI.target.position, transform.position);
        
        if(distance < Settings.Astar.PursueDistance)
        {
            _enemyAI.Transition(State.PursueState);
        }
    }

구현 이미지

몬스터가 플레이어를 추적하다가, 가까워지면 돌진하도록 FSM을 만들구 구현해보았습니다.

용량문제로 15fps라 조금 버벅거려 보이긴 하지만 실제로는 잘 작동해서 만족스러웠습니다.

최근댓글

최근글

skin by © 2024 ttuttak