UnityPackages

StateKraft

Project Details


Production Time - ongoing

Team Size - 2

Engine - Unity

Example


Here for example a basic state machine is used for handeling a player characters air- and ground state.


The states are saved as unity scriptabled objects and are shown in the inspector of the monobehaviour that holds a state machine. The inspector also shows the current state that the state machine is executing.



Description


StateKraft is a StateMachine plugin for Unity, over the years I've often needed to use a state machine for use with for example character controllers or menu controllers.


So me and a couple of friends devolped a generalized state machine that we've used during many game projects.




using UnityEngine;

namespace StateKraft
{
    [System.Serializable]
    public partial class State : ScriptableObject
    {
        [HideInInspector] public ushort Id;
        public StateMachine StateMachine { get; private set; }
        protected GameObject gameObject;
        protected Transform transform;

        public void InternalInitialize(object owner, StateMachine stateMachine)
        {
            StateMachine = stateMachine;
            if (!(owner is MonoBehaviour mono)) return;
            gameObject = mono.gameObject;
            transform = mono.transform;
        }
        public virtual void Initialize(object owner) { }
        public virtual void Enter() { }
        public virtual void StateUpdate() { }
        public virtual void Exit() { }

        public void TransitionTo()
        {
            StateMachine.TransitionTo(this);
        }
        public void TransitionTo<T>()
        {
            StateMachine.TransitionTo<T>();
        }
        public T GetState<T>()
        {
            return StateMachine.GetState<T>();
        }
        public void ForceState()
        {
            StateMachine.ForceState(this);
        }
        public void ForceState<T>()
        {
            StateMachine.ForceState<T>();
        }
    }
}


    


using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Object = UnityEngine.Object;
#pragma warning disable 649

namespace StateKraft
{
    [Serializable]
    public class StateMachine
    {
        [SerializeField] private State[] _states;
        private Dictionary<Type, State> _stateDictionary;
        private object _owner;
        public State CurrentState { get; private set; }
        private bool _runFirstEnter = true;

        public void Initialize(object owner)
        {
            _owner = owner;
            _stateDictionary = new Dictionary<Type, State>();
            //Create copies of all states
            State firstState = null;
            ushort index = 0;
            foreach (State state in _states)
            {
                State instance = Object.Instantiate(state);
                if (firstState == null) firstState = instance;
                Type type = state.GetType();
                _stateDictionary[type] = instance;
                _stateById[index] = type;
                _idByState[type] = index;
                instance.Id = index;
                index++;
            }
            //Run init and set the state machine variable of all created states
            foreach (State state in _stateDictionary.Values)
            {
                state.InternalInitialize(owner, this);
                state.Initialize(owner);
            }
        }
        public void Update()
        {
            if (_runFirstEnter)
            {
                TransitionTo(_stateDictionary.Values.FirstOrDefault());
                _runFirstEnter = false;
            }
            
            if (CurrentState != null) 
                CurrentState.StateUpdate();
        }

        public T GetState<T>()
        {
            return (T)Convert.ChangeType(_stateDictionary[typeof(T)], typeof(T));
        }
        public State GetState(Type type)
        {
            return _stateDictionary[type];
        }

        public void TransitionTo(ushort id)
        {
            TransitionTo(GetState(_stateById[id]));
        }
        public void TransitionTo<T>()
        {
            TransitionTo(GetState<T>() as State);
        }
        public void TransitionTo(State state)
        {
            if (state == null) { Debug.LogWarning("Cannot transition to state null"); return; }
            if (CurrentState != null) CurrentState.Exit();
            CurrentState = state;
            CurrentState.Enter();
        }

        public ushort GetId(State state)
        {
            return _idByState[state.GetType()];
        }
        public State GetState(ushort id)
        {
            return GetState(_stateById[id]);
        }
    }
}