디자인패턴

상태패턴

khkg12 2023. 11. 13. 21:49

상태패턴이란?

상태패턴은 한 객체가 여러 개의 상태를 가질 때 사용하는 패턴이다. 예를 들어 몬스터가 탐지, 추격, 공격이라는 상태를 가진다고 했을 때 탐지 상태 중 적 발견 시 추격으로 전환, 추격 후 공격범위 안에 들어올 시 공격 상태로 전환 등 특정 조건에 따라 상태가 변경되고 그 객체는 하나의 상태만을 가지고 있을 때 사용된다.

 

예시코드

using System;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

public interface IStateMachine
{
    void SetState(string name);
    object GetOwner();
}

public class State
{
    public IStateMachine sm = null;
    public event Action onEnter;
    public virtual void Enter()
    {
        onEnter?.Invoke();
    }

    public virtual void Update()
    {

    }

    public virtual void Exit()
    {

    }

    public virtual void Init(IStateMachine sm)
    {
        this.sm = sm;
    }

    public void AddListner(Action action)
    {
        onEnter += action;
    }    
}

public class GMState : State
{
    protected GameManager gm;    

    public override void Init(IStateMachine sm)
    {
        base.Init(sm);
        gm = (GameManager)sm.GetOwner();
    }
}

public class DefaultState : State
{
    public override void Update()
    {
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            sm.SetState("Play"); // Add과정에서 sm이 누군지 알려주었기 때문에 null이 아니게 되고 사용할수 있게된다.
        }
    }
}

public class PlayState : State
{
    public override void Update()
    {
        ((GameManager)sm.GetOwner()).score = 0; // 소유주를 알고있기 때문에 가능
    }
}

public class StateMachine<T> : IStateMachine where T : Component // 보통은 소유주가 우리가 만든 스크립트일 가능성이 높기 때문에 컴포넌트로 좁혀줘도됨
{
    public T owner = null;
    public State curState = null;
    public Dictionary<string, State> stateDic;

    public StateMachine()
    {
        stateDic = new Dictionary<string, State>(); 
    }

    public void AddState(string name, State state)
    {
        if (stateDic.ContainsKey(name))
            return;
        state.sm = this;
        stateDic.Add(name, state);
    }

    public object GetOwner()
    {
        return owner; // GameManager든 Player든 박싱이 일어나서 올라감   
    }

    public void SetState(string name)
    {
        if (stateDic.ContainsKey(name))
        {
            curState?.Exit();
            curState = stateDic[name];
            curState.Enter();
        }            
    }

    public void Update()
    {
        curState.Update();
    }
}


public class GameManager : MonoBehaviour
{    
    public static GameManager instance = null;

    public int score = 0;

    StateMachine<GameManager> sm;

    private void Awake()
    {
        if (instance == null)
        {
            instance = this;
            DontDestroyOnLoad(gameObject);
        }                        
        else
            Destroy(gameObject);
        sm = new StateMachine<GameManager>();
        sm.owner = this;

        
        sm.AddState("Default", new DefaultState());
        sm.AddState("Play", new PlayState());
        sm.SetState("Default");

        //PlayState ps = new PlayState();
        //ps.onEnter += () => { Debug.Log("asdsadasdasd"); };
        sm.stateDic["Default"].AddListner(() => { Debug.Log("asdasd"); }); // AddListner적용, 위 주석 대체
        sm.stateDic["Play"].AddListner(() => { score = 0; });
    }

    private void Update()
    {
        sm.Update();
    }

}

'디자인패턴' 카테고리의 다른 글

반복자패턴  (0) 2023.11.23
옵저버패턴  (0) 2023.11.10
오브젝트 풀링  (0) 2023.10.05
어댑터 패턴  (0) 2023.10.03
전략패턴  (0) 2023.09.27