Skip to content

Instantly share code, notes, and snippets.

@takumifukasawa
Last active January 21, 2023 06:51
Show Gist options
  • Save takumifukasawa/3ee9ba66eba90b5b3a188a5f0b717904 to your computer and use it in GitHub Desktop.
Save takumifukasawa/3ee9ba66eba90b5b3a188a5f0b717904 to your computer and use it in GitHub Desktop.
unity simple state machine
using System;
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
namespace Utilities
{
public class StateMachine<T> where T : IConvertible
{
public interface IState
{
void Enter();
void Update();
void FixedUpdate();
}
public class State : IState
{
private T _state;
public System.Action OnEnter;
public System.Action OnLeave;
public System.Action OnUpdate;
public System.Action OnFixedUpdate;
public State(T state)
{
_state = state;
}
public void Enter()
{
OnEnter?.Invoke();
}
public void Leave()
{
OnLeave?.Invoke();
}
public void Update()
{
OnUpdate?.Invoke();
}
public void FixedUpdate()
{
OnFixedUpdate?.Invoke();
}
}
private Dictionary<T, State> _states = new Dictionary<T, State>();
private T _currentState = default(T);
private T _willNextState = default(T);
public System.Action<T, T> OnBeforeChangeState;
public System.Action OnAfterChangeState;
public System.Action<T, T> OnSwitchedState;
public T WillNextState
{
get { return _willNextState; }
}
public T CurrentState
{
get { return _currentState; }
}
public StateMachine(T initialState)
{
var stateNames = System.Enum.GetValues(typeof(T)).Cast<T>().ToList();
stateNames.ForEach(state =>
{
CreateState(state);
});
// set initial state
_currentState = initialState;
}
public State CreateState(T s)
{
State state = new State(s);
_states.Add(s, state);
return state;
}
public State GetState(T s)
{
State state;
if (_states.TryGetValue(s, out state))
{
return state;
}
// TODO: throw error
Debug.LogError("invalid state");
return null;
}
/// <summary>
/// stateを切り替えるときは必ずここを通す
/// </summary>
/// <param name="s"></param>
/// <param name="isImmediately"></param>
public void ChangeState(T s, bool isImmediately = false)
{
_willNextState = s;
if (isImmediately)
{
SwitchState();
}
}
void SwitchState()
{
T beforeState = _currentState;
if (OnBeforeChangeState != null)
{
OnBeforeChangeState.Invoke(_currentState, _willNextState);
}
_states[_currentState].Leave();
_currentState = _willNextState;
_willNextState = default(T);
if (OnSwitchedState != null)
{
OnSwitchedState.Invoke(beforeState, _currentState);
}
_states[_currentState].Enter();
if (OnAfterChangeState != null)
{
OnAfterChangeState.Invoke();
}
}
public void Update()
{
_states[_currentState].Update();
}
void SwitchStateIfNeeded()
{
if (!EqualityComparer<T>.Default.Equals(_willNextState, default(T)))
{
SwitchState();
}
}
public void Step()
{
// NOTE: fixed update で呼んで大丈夫?
SwitchStateIfNeeded();
}
public void FixedUpdate()
{
_states[_currentState].FixedUpdate();
}
public bool EqualsCurrentState(T s)
{
return EqualityComparer<T>.Default.Equals(_currentState, s);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment