C# Unity FSM 状态机

C# Unity FSM 状态机

使用状态机可以降低代码耦合性,并且可以优化代码可读性,方便团队协作等。

对于游戏开发内容来讲游戏开发的流程控制玩家动画都可以使用FSM有限状态机来实现。

1.FsmState

每个状态的基类,泛型参数表示所拥有者

csharp 复制代码
public abstract class FsmState<T> where T : class
{
    protected internal abstract void OnInit(IFsm<T> fsm);
    protected internal abstract void OnEnter(IFsm<T> fsm);
    protected internal abstract void OnUpdate(IFsm<T> fsm);
    protected internal abstract void OnLeave(IFsm<T> fsm);
    protected internal abstract void OnDestroy(IFsm<T> fsm);

    protected void ChangeState<TState>(IFsm<T> fsm) where TState : FsmState<T>
    {
        Fsm<T> fsmImplement = (Fsm<T>)fsm;
        if(fsmImplement == null)
        {
            throw new Exception("FSM is invalid.");
        }
        fsmImplement.ChangeState<TState>();
    }
    protected void ChangeState(IFsm<T> fsm, Type stateType)
    {
        Fsm<T> fsmImplement = (Fsm<T>)fsm;
        if (fsmImplement == null)
        {
            throw new Exception("FSM is invalid.");
        }

        if (stateType == null)
        {
            throw new Exception("State type is invalid.");
        }

        if (!typeof(FsmState<T>).IsAssignableFrom(stateType))
        {
            throw new Exception("State type is invalid.");
        }

        fsmImplement.ChangeState(stateType);
    }
}

2.IFsm

有限状态机的接口

csharp 复制代码
public interface IFsm<T> where T : class
{
    string Name
    {
        get;
    }
    string FullName
    {
        get;
    }
    T Owner
    {
        get;
    }
    int FsmStateCount
    {
        get;
    }
    bool IsRunning
    {
        get;
    }
    bool IsDestroyed
    {
        get;
    }
    FsmState<T> CurrentState
    {
        get;
    }
    float CurrentStateTime
    {
        get;
    }

    void Start<TState>() where TState : FsmState<T>;
    bool HasState<TState>() where TState : FsmState<T>;
    TState GetState<TState>() where TState : FsmState<T>;
    FsmState<T> GetState(Type stateType);
    FsmState<T>[] GetAllStates();
}

3.IFsmManager

有限状态机管理器接口

csharp 复制代码
public interface IFsmManager
{
    int Count { get; }
    bool HasFsm<T>() where T : class;
    bool HasFsm(Type ownerType);
    bool HasFsm<T>(string name) where T : class;
    bool HasFsm(Type ownerType, string name);

    IFsm<T> GetFsm<T>() where T : class;
    IFsm<T> GetFsm<T>(string name) where T : class;

    IFsm<T> CreateFsm<T>(T owner, params FsmState<T>[] fsmStates) where T : class;
    IFsm<T> CreateFsm<T>(string name, T owner, params FsmState<T>[] states) where T : class;
    IFsm<T> CreateFsm<T>(T owner, List<FsmState<T>> states) where T : class;
    IFsm<T> CreateFsm<T>(string name, T owner, List<FsmState<T>> states) where T : class;

    bool DestroyFsm<T>() where T : class;
    bool DestroyFsm(Type ownerType);
    bool DestroyFsm<T>(string name) where T : class;
    bool DestroyFsm(Type ownerType, string name);
    bool DestroyFsm<T>(IFsm<T> fsm) where T : class;
}

4.FsmBase

有限状态机的基类

csharp 复制代码
public abstract class FsmBase
{
    private string m_Name;

    public FsmBase()
    {
        m_Name = string.Empty;
    }

    public string Name
    {
        get
        {
            return m_Name;
        }
        protected set
        {
            m_Name = value ?? string.Empty;
        }
    }

    public string FullName
    {
        get
        {
            return $"{OwnerType.FullName}.{Name}";
        }
    }

    public abstract Type OwnerType
    {
        get;
    }

    public abstract int FsmStateCount
    {
        get;
    }

    public abstract bool IsRunning
    {
        get;
    }

    public abstract bool IsDestroyed
    {
        get;
    }

    public abstract string CurrentStateName
    {
        get;
    }

    public abstract float CurrentStateTime
    {
        get;
    }

    public abstract void Update();
    public abstract void Shutdown();
}

5.Fsm

状态机类

csharp 复制代码
public class Fsm<T> : FsmBase, IFsm<T> where T : class
{
    private T m_Owner;
    private readonly Dictionary<Type, FsmState<T>> m_States;
    private FsmState<T> m_CurrentState;
    private float m_CurrentStateTime;
    private bool m_IsDestroyed;
    public Fsm()
    {
        m_Owner = null;
        m_States = new Dictionary<Type, FsmState<T>>();
        m_CurrentState = null;
        m_CurrentStateTime = 0f;
        m_IsDestroyed = true;
    }
    public T Owner => m_Owner;
    public FsmState<T> CurrentState => m_CurrentState;
    public override Type OwnerType => typeof(T);
    public override int FsmStateCount => m_States.Count;
    public override bool IsRunning => m_CurrentState != null;
    public override bool IsDestroyed => m_IsDestroyed;
    public override string CurrentStateName => m_CurrentState != null ? m_CurrentState.GetType().FullName : null;
    public override float CurrentStateTime => m_CurrentStateTime;
    public static Fsm<T> Create(string name,T owner,params FsmState<T>[] states)
    {
        if(owner == null)
        {
            throw new Exception("FSM owner is invalid.");
        }
        if(states== null|| states.Length < 1)
        {
            throw new Exception("FSM states is invalid.");
        }

        Fsm<T> fsm = Pool<Fsm<T>>.Rent();
        fsm.Name = name;
        fsm.m_Owner = owner;
        fsm.m_IsDestroyed = false;
        foreach (FsmState<T> oneState in states)
        {
            if(oneState == null)
            {
                throw new Exception("FSM states is invalid.");
            }

            Type stateType = oneState.GetType();
            if (fsm.m_States.ContainsKey(stateType))
            {
                throw new Exception($"{stateType} state is already exist");
            }
            fsm.m_States.Add(stateType, oneState);
            oneState.OnInit(fsm);
        }
        return fsm;
    }
    public static Fsm<T> Create(string name,T owner,List<FsmState<T>> states)
    {
        if (owner == null)
        {
            throw new Exception("FSM owner is invalid.");
        }
        if (states == null || states.Count < 1)
        {
            throw new Exception("FSM states is invalid.");
        }

        Fsm<T> fsm = Pool<Fsm<T>>.Rent();
        fsm.Name = name;
        fsm.m_Owner = owner;
        fsm.m_IsDestroyed = false;
        foreach (FsmState<T> oneState in states)
        {
            if (oneState == null)
            {
                throw new Exception("FSM states is invalid.");
            }

            Type stateType = oneState.GetType();
            if (fsm.m_States.ContainsKey(stateType))
            {
                throw new Exception($"{stateType} state is already exist");
            }
            fsm.m_States.Add(stateType, oneState);
            oneState.OnInit(fsm);
        }
        return fsm;
    }
    public FsmState<T>[] GetAllStates()
    {
        int index = 0;
        FsmState<T>[] arr = new FsmState<T>[m_States.Count];
        foreach (FsmState<T> fsmState in m_States.Values)
        {
            arr[index++] = fsmState;
        }
        return arr;
    }
    public bool HasState<TState>() where TState : FsmState<T>
    {
        return m_States.ContainsKey(typeof(TState));
    }
    public override void Shutdown()
    {
        Pool<Fsm<T>>.Return(this, (fsm) =>
         {
             if(m_CurrentState != null)
             {
                 m_CurrentState.OnLeave(this);
             }
             foreach (FsmState<T> oneState in m_States.Values)
             {
                 oneState.OnDestroy(this);
             }

             Name = null;
             m_Owner = null;
             m_States.Clear();



             m_CurrentState = null;
             m_CurrentStateTime = 0f;
             m_IsDestroyed = true;
         });
    }
    public void Start<TState>() where TState : FsmState<T>
    {
        if (IsRunning)
        {
            throw new Exception("FSM is running, can not start again.");
        }
        FsmState<T> state = GetState<TState>();
        if (state == null)
        {
            throw new Exception("can not start state");
        }
        m_CurrentStateTime = 0f;
        m_CurrentState = state;
        m_CurrentState.OnEnter(this);
    }
    public void Start(Type stateType)
    {
        if (IsRunning)
        {
            throw new Exception("FSM is running, can not start again.");
        }

        if (stateType == null)
        {
            throw new Exception("State type is invalid.");
        }

        if (!typeof(FsmState<T>).IsAssignableFrom(stateType))
        {
            throw new Exception("State type is invalid.");
        }

        FsmState<T> state = GetState(stateType);
        if (state == null)
        {
            throw new Exception("FSM can not start state which is not exist.");
        }

        m_CurrentStateTime = 0f;
        m_CurrentState = state;
        m_CurrentState.OnEnter(this);
    }
    public override void Update()
    {
        m_CurrentStateTime += Time.deltaTime;
        m_CurrentState.OnUpdate(this);
    }
    public TState GetState<TState>() where TState : FsmState<T>
    {
        if (m_States.TryGetValue(typeof(TState), out FsmState<T> fsmState))
        {
            return (TState)fsmState;
        }
        return null;
    }
    public FsmState<T> GetState(Type stateType)
    {
        if (stateType == null)
        {
            throw new Exception("State type is invalid.");
        }

        if (!typeof(FsmState<T>).IsAssignableFrom(stateType))
        {
            throw new Exception("State type is invalid.");
        }

        if (m_States.TryGetValue(stateType, out FsmState<T> fsmState))
        {
            return fsmState;
        }
        return null;
    }
    public void ChangeState<TState>()
    {
        ChangeState(typeof(TState));
    }
    public void ChangeState(Type stateType)
    {
        if (m_CurrentState == null)
        {
            throw new Exception("Current state is invalid.");
        }

        FsmState<T> state = GetState(stateType);
        if (state == null)
        {
            throw new Exception("FSM can not change state which is not exist.");
        }

        m_CurrentState.OnLeave(this);
        m_CurrentStateTime = 0f;
        m_CurrentState = state;
        m_CurrentState.OnEnter(this);
    }
}

6.FsmManager

状态机管理器

csharp 复制代码
public class FsmManager : Singleton<FsmManager>,IFsmManager,IUpdateSingleton
{
    private readonly Dictionary<string, FsmBase> m_FsmDic;
    private readonly List<FsmBase> m_TempFsms;
    public FsmManager()
    {
        m_FsmDic = new Dictionary<string, FsmBase>();
        m_TempFsms = new List<FsmBase>();
    }
    public int Count => m_FsmDic.Count;

    public IFsm<T> CreateFsm<T>(T owner, params FsmState<T>[] fsmStates) where T : class
    {
        return CreateFsm(string.Empty, owner, fsmStates);
    }
    public IFsm<T> CreateFsm<T>(string name, T owner, params FsmState<T>[] states) where T : class
    {
        if (HasFsm<T>(name))
        {
            throw new Exception("Already exist FSM");
        }
        Fsm<T> fsm = Fsm<T>.Create(name, owner, states);
        m_FsmDic.Add(fsm.FullName, fsm);
        return fsm;
    }
    public IFsm<T> CreateFsm<T>(T owner, List<FsmState<T>> states) where T : class
    {
        return CreateFsm(string.Empty, owner, states);
    }
    public IFsm<T> CreateFsm<T>(string name, T owner, List<FsmState<T>> states) where T : class
    {
        if (HasFsm<T>(name))
        {
            throw new Exception("Already exist FSM");
        }
        Fsm<T> fsm = Fsm<T>.Create(name, owner, states);
        m_FsmDic.Add(fsm.FullName, fsm);
        return fsm;
    }

    public bool DestroyFsm<T>() where T : class
    {
        return InternalDestroyFsm($"{typeof(T).FullName}.{string.Empty}");
    }
    public bool DestroyFsm(Type ownerType)
    {
        if (ownerType == null)
        {
            throw new Exception("Owner type is invalid.");
        }
        return InternalDestroyFsm($"{ownerType.FullName}.{string.Empty}");
    }
    public bool DestroyFsm<T>(string name) where T : class
    {
        return InternalDestroyFsm($"{typeof(T).FullName}.{name}");
    }
    public bool DestroyFsm(Type ownerType, string name)
    {
        if (ownerType == null)
        {
            throw new Exception("Owner type is invalid.");
        }
        return InternalDestroyFsm($"{ownerType.FullName}.{name}");
    }
    public bool DestroyFsm<T>(IFsm<T> fsm) where T : class
    {
        if (fsm == null)
        {
            throw new Exception("FSM is invalid.");
        }
        return InternalDestroyFsm(fsm.FullName);
    }

    public IFsm<T> GetFsm<T>() where T : class
    {
        return (IFsm<T>)InternalGetFsm($"{typeof(T).FullName}.{string.Empty}");
    }
    public IFsm<T> GetFsm<T>(string name) where T : class
    {
        return (IFsm<T>)InternalGetFsm($"{typeof(T).FullName}.{name}");
    }

    public bool HasFsm<T>() where T : class
    {
        return InternalHasFsm($"{typeof(T).FullName}.{string.Empty}");
    }
    public bool HasFsm(Type ownerType)
    {
        if(ownerType == null)
        {
            throw new Exception("Owner type is invalid.");
        }
        return InternalHasFsm($"{ownerType.FullName}.{string.Empty}");
    }
    public bool HasFsm<T>(string name) where T : class
    {
        return InternalHasFsm($"{typeof(T).FullName}.{name}");
    }
    public bool HasFsm(Type ownerType, string name)
    {
        if (ownerType == null)
        {
            throw new Exception("Owner type is invalid.");
        }
        return InternalHasFsm($"{ownerType.FullName}.{name}");
    }


    private bool InternalDestroyFsm(string name)
    {
        if(m_FsmDic.TryGetValue(name,out FsmBase fsmBase))
        {
            fsmBase.Shutdown();
            return m_FsmDic.Remove(name);
        }
        return false;
    }
    private FsmBase InternalGetFsm(string name)
    {
        FsmBase fsm = null;
        if (m_FsmDic.TryGetValue(name, out fsm))
        {
            return fsm;
        }

        return null;
    }
    private bool InternalHasFsm(string name)
    {
        return m_FsmDic.ContainsKey(name);
    }

    public void Update()
    {
        m_TempFsms.Clear();
        if (m_FsmDic.Count <= 0)
            return;


        foreach (FsmBase fsmBase in m_FsmDic.Values)
        {
            m_TempFsms.Add(fsmBase);
        }

        foreach (FsmBase fsmBase in m_TempFsms)
        {
            if (fsmBase.IsDestroyed)
                continue;
            fsmBase.Update();
        }
    }

    protected override void Load(int assemblyName)
    {
        
    }

    protected override void UnLoad(int assemblyName)
    {
        
    }
}

7.测试

(一)IdleState

csharp 复制代码
public class IdleState : FsmState<FsmTest>
{
    protected internal override void OnDestroy(IFsm<FsmTest> fsm)
    {
        Debug.Log("销毁 IdleState");
    }

    protected internal override void OnEnter(IFsm<FsmTest> fsm)
    {
        Debug.Log("进入 IdleState");
    }

    protected internal override void OnInit(IFsm<FsmTest> fsm)
    {
    }

    protected internal override void OnLeave(IFsm<FsmTest> fsm)
    {
        Debug.Log("离开 IdleState");
    }

    protected internal override void OnUpdate(IFsm<FsmTest> fsm)
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            ChangeState<WalkState>(fsm);
        }
    }
}

(二)WalkState

csharp 复制代码
public class WalkState : FsmState<FsmTest>
{
    protected internal override void OnDestroy(IFsm<FsmTest> fsm)
    {
        Debug.Log("销毁 WalkState");
    }

    protected internal override void OnEnter(IFsm<FsmTest> fsm)
    {
        Debug.Log("进入 WalkState");
    }

    protected internal override void OnInit(IFsm<FsmTest> fsm)
    {
    }

    protected internal override void OnLeave(IFsm<FsmTest> fsm)
    {
        Debug.Log("离开 WalkState");
    }

    protected internal override void OnUpdate(IFsm<FsmTest> fsm)
    {
        if (Input.GetKeyDown(KeyCode.B))
        {
            ChangeState<RunState>(fsm);
        }
    }
}

(三)RunState

csharp 复制代码
public class RunState : FsmState<FsmTest>
{
    protected internal override void OnDestroy(IFsm<FsmTest> fsm)
    {
        Debug.Log("销毁 RunState");
    }

    protected internal override void OnEnter(IFsm<FsmTest> fsm)
    {
        Debug.Log("进入 RunState");
    }

    protected internal override void OnInit(IFsm<FsmTest> fsm)
    {
    }

    protected internal override void OnLeave(IFsm<FsmTest> fsm)
    {
        Debug.Log("离开 RunState");
    }

    protected internal override void OnUpdate(IFsm<FsmTest> fsm)
    {
        if (Input.GetKeyDown(KeyCode.C))
        {
            ChangeState<IdleState>(fsm);
        }
    }
}

mono测试

csharp 复制代码
public class FsmTest : MonoBehaviour
{
    private IFsm<FsmTest> m_TestFsm;
    void Start()
    {
        SingletonSystem.Initialize();
        AssemblyManager.Load(1, GetType().Assembly);

        m_TestFsm = FsmManager.Instance.CreateFsm<FsmTest>("MyTestFsm",this, new IdleState(),new WalkState(),new RunState());
        m_TestFsm.Start<IdleState>();
    }

    void Update()
    {
        SingletonSystem.Update();
        if (Input.GetKeyDown(KeyCode.P))
        {
            FsmManager.Instance.DestroyFsm(m_TestFsm);
        }
    }
}
相关推荐
小码编匠2 小时前
一款 C# 编写的神经网络计算图框架
后端·神经网络·c#
Envyᥫᩣ5 小时前
C#语言:从入门到精通
开发语言·c#
小春熙子10 小时前
Unity图形学之Shader结构
unity·游戏引擎·技术美术
IT技术分享社区11 小时前
C#实战:使用腾讯云识别服务轻松提取火车票信息
开发语言·c#·云计算·腾讯云·共识算法
Sitarrrr12 小时前
【Unity】ScriptableObject的应用和3D物体跟随鼠标移动:鼠标放置物体在场景中
3d·unity
极梦网络无忧12 小时前
Unity中IK动画与布偶死亡动画切换的实现
unity·游戏引擎·lucene
△曉風殘月〆18 小时前
WPF MVVM入门系列教程(二、依赖属性)
c#·wpf·mvvm
逐·風20 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#
_oP_i1 天前
Unity Addressables 系统处理 WebGL 打包本地资源的一种高效方式
unity·游戏引擎·webgl