콘텍스트와 구상 상태들 모두 콘텍스트의 다음 상태를 설정할 수 있으며, 콘텍스트에 연결된 상태 객체를 교체하여 실제 상태 천이를 수행할 수 있다.
namespace StateMachine
{
public abstract class State<T>
{
protected StateMachine<T> _stateMachine;
protected T _context;
// 상태 설정 함수
internal void SetContext(StateMachine<T> stateMachine, T context)
{
this._stateMachine = stateMachine;
this._context = context;
OnInitialized();
}
// 상태를 초기화시킬 때 호출되는 함수 (Awake와 유사)
public virtual void OnInitialized()
{}
// 상태에 진입할 때 호출되는 함수 (Start와 유사)
public virtual void OnEnter()
{}
// 상태의 Update 함수, 이 곳에서 다른 상태로의 전환 조건을 걸 수 있음
public abstract void Update();
public virtual void FixedUpdate()
{}
// 상태를 종료할 때 호출되는 함수
public virtual void OnExit()
{}
}
}
namespace StateMachine
{
public class StateMachine<T>
{
private T _context;
private State<T> _currentState;
public State<T> CurrentState => _currentState;
private State<T> _previousState;
public State<T> PreviousState => _previousState;
private Dictionary<Type, State<T>> _states = new ();
#region Constructor
public StateMachine(T context, State<T> initialState)
{
this._context = context;
AddState(initialState);
_currentState = initialState;
_currentState.OnEnter();
}
#endregion
#region State Modifying Methods
public void AddState(State<T> state)
{
state.SetContext(this, _context);
_states[state.GetType()] = state;
}
public void RemoveState(State<T> state)
{
foreach (var st in _states)
{
if (st.Key == state.GetType())
{
_states.Remove(state.GetType());
break;
}
}
}
public void RemoveAllState()
{
_states.Clear();
}
public void ChangeState<T2>() where T2 : State<T>
{
if (typeof(T2) == _currentState.GetType())
return;
if (_currentState != null)
_currentState.OnExit();
_previousState = _currentState;
_currentState = _states[typeof(T2)];
_currentState.OnEnter();
}
#endregion
#region Update State Methods
public void Update()
{
_currentState.Update();
}
public void FixedUpdate()
{
_currentState.FixedUpdate();
}
#endregion
#region Check State Methods
public bool IsCurrentState<T2>() where T2 : State<T>
{
if (typeof(T2) == _currentState.GetType())
{
return true;
}
return false;
}
#endregion
}
}