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