【SKFramework框架核心模块】3-6、FSM有限状态机模块

推荐阅读

大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。

一、前言

【Unity3D框架】SKFramework框架完全教程《全网最全》-专栏文章目录:
https://blog.csdn.net/q764424567/article/details/143926557

二、正文

2-1、介绍

有限状态机(Finite State Machine,FSM),是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。

有限状态机的核心原理是基于状态和状态之间的转换,可以用来描述系统的行为和流程,尤其是在处理离散事件和复杂逻辑时代码有较强的可维护性及健壮性。

有限状态机作为一种强大的工具,被广泛用于管理游戏对象的状态转换和行为。

本章就对SKFramework框架FSM有限状态机模块进行讲解。

2-2、使用说明

使用示例:

csharp 复制代码
using SK.Framework;
using SK.Framework.FSM;
using UnityEngine;

public class UseFSM : MonoBehaviour
{
    FSM fsm;
    public class TestState : State
    {
        public string stringValue;
    }

    private void Start()
    {
        fsm = SKFramework.Module<FSM>();
        //创建状态机
        var machine = fsm.Create<StateMachine>("示例状态机")
            //构建状态一
            .Build<TestState>("状态一")
                //设置状态一初始化事件
                .OnInitialization(state => state.stringValue = "A")
                //设置状态一进入事件
                .OnEnter(state => Debug.Log("进入状态一"))
                //设置状态一停留事件
                .OnStay(state => Debug.Log("状态一"))
                //设置状态一推出事件
                .OnExit(state => Debug.Log("退出状态一"))
                //设置状态一销毁事件
                .OnTermination(state => state.stringValue = null)
            //状态一构建完成
            .Complete()
            //构建状态二
            .Build<State>("状态二")
                //设置状态二进入事件
                .OnEnter(state => Debug.Log("进入状态二"))
                //设置状态二停留事件
                .OnStay(state => Debug.Log("状态二"))
                //设置状态二退出事件
                .OnExit((state => Debug.Log("退出状态二")))
            //状态二构建完成
            .Complete()
            //构建状态三
            .Build<State>("状态三")
                //设置状态三进入事件
                .OnEnter(state => Debug.Log("进入状态三"))
                //设置状态三停留事件
                .OnStay(state => Debug.Log("状态三"))
                //设置状态三退出事件
                .OnExit((state => Debug.Log("退出状态三")))
            //状态三构建完成
            .Complete()
            //添加状态切换条件 当按下快捷键1时 切换至状态一
            .SwitchWhen(() => Input.GetKeyDown(KeyCode.Alpha1), "状态一")
            //添加状态切换条件 当按下快捷键2时 切换至状态二
            .SwitchWhen(() => Input.GetKeyDown(KeyCode.Alpha2), "状态二")
            //添加状态切换条件 当按下快捷键3时 切换至状态三
            .SwitchWhen(() => Input.GetKeyDown(KeyCode.Alpha3), "状态三")
            //为状态一至状态二添加切换条件:若当前状态为状态一时 按下快捷键4 切换至状态二
            .SwitchWhen(() => Input.GetKeyDown(KeyCode.Alpha4), "状态一", "状态二");
    }
}

运行结果:

2-3、实现及代码分析

状态接口类:

csharp 复制代码
namespace FSM
{
    public interface IStateData { }
}

抽象接口类:

csharp 复制代码
using FSM;
using System;

namespace FSM
{
    /// <summary>
    /// 抽象状态类
    /// </summary>
    public class State
    {
        /// <summary>
        /// 状态名称
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 是否可切换至自身
        /// </summary>
        public virtual bool CanSwitch2Self { get; set; }
        /// <summary>
        /// 所属状态机
        /// </summary>
        public StateMachine Machine { get; internal set; }
        /// <summary>
        /// 状态初始化事件
        /// </summary>
        internal Action onInitialization;
        /// <summary>
        /// 状态进入事件
        /// </summary>
        internal Action onEnter;
        /// <summary>
        /// 状态停留事件
        /// </summary>
        internal Action onStay;
        /// <summary>
        /// 状态退出事件
        /// </summary>
        internal Action onExit;
        /// <summary>
        /// 状态终止事件
        /// </summary>
        internal Action onTermination;

        /// <summary>
        /// 状态初始化事件
        /// </summary>
        public virtual void OnInitialization()
        {
            onInitialization?.Invoke();
        }
        /// <summary>
        /// 状态进入事件
        /// </summary>
        public virtual void OnEnter(IStateData data = null)
        {
            onEnter?.Invoke();
        }
        /// <summary>
        /// 状态停留事件
        /// </summary>
        public virtual void OnStay()
        {
            onStay?.Invoke();
        }
        /// <summary>
        /// 状态退出事件
        /// </summary>
        public virtual void OnExit()
        {
            onExit?.Invoke();
        }
        /// <summary>
        /// 状态终止事件
        /// </summary>
        public virtual void OnTermination()
        {
            onTermination?.Invoke();
        }
        /// <summary>
        /// 设置状态切换条件
        /// </summary>
        /// <param name="predicate">切换条件</param>
        /// <param name="targetStateName">目标状态名称</param>
        public void SwitchWhen(Func<bool> predicate, string targetStateName)
        {
            Machine.SwitchWhen(predicate, Name, targetStateName);
        }
    }
}

核心事件:

  • OnInitialization 状态初始化事件
  • OnEnter 状态进入事件
  • OnStay 状态停留事件
  • OnExit 状态退出事件
  • OnTermination 状态终止事件

状态切换类(StateSwitchCondition):

csharp 复制代码
using System;

namespace FSM
{
    public class StateSwitchCondition
    {
        public readonly Func<bool> predicate;

        public readonly string sourceStateName;

        public readonly string targetStateName;

        public StateSwitchCondition(Func<bool> predicate, string sourceStateName, string targetStateName)
        {
            this.predicate = predicate;
            this.sourceStateName = sourceStateName;
            this.targetStateName = targetStateName;
        }
    }
}

状态构建器(StateBuilder):

csharp 复制代码
using System;

namespace FSM
{
    /// <summary>
    /// 状态构建器
    /// </summary>
    /// <typeparam name="T">状态类型</typeparam>
    public class StateBuilder<T> where T : State, new()
    {
        //构建的状态
        private readonly T state;
        //构建的状态所属的状态机
        private readonly StateMachine stateMachine;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="state"></param>
        /// <param name="stateMachine"></param>
        public StateBuilder(T state, StateMachine stateMachine)
        {
            this.state = state;
            this.stateMachine = stateMachine;
        }

        /// <summary>
        /// 设置状态初始化事件
        /// </summary>
        /// <param name="onInitialization">状态初始化事件</param>
        /// <returns>状态构建器</returns>
        public StateBuilder<T> OnInitialization(Action<T> onInitialization)
        {
            state.onInitialization = () => onInitialization(state);
            return this;
        }
        /// <summary>
        /// 设置状态进入事件
        /// </summary>
        /// <param name="onEnter">状态进入事件</param>
        /// <returns>状态构建器</returns>
        public StateBuilder<T> OnEnter(Action<T> onEnter)
        {
            state.onEnter = () => onEnter(state);
            return this;
        }
        /// <summary>
        /// 设置状态停留事件
        /// </summary>
        /// <param name="onStay">状态停留事件</param>
        /// <returns>状态构建器</returns>
        public StateBuilder<T> OnStay(Action<T> onStay)
        {
            state.onStay = () => onStay(state);
            return this;
        }
        /// <summary>
        /// 设置状态退出事件
        /// </summary>
        /// <param name="onExit">状态退出事件</param>
        /// <returns>状态构建器</returns>
        public StateBuilder<T> OnExit(Action<T> onExit)
        {
            state.onExit = () => onExit(state);
            return this;
        }
        /// <summary>
        /// 设置状态终止事件
        /// </summary>
        /// <param name="onTermination">状态终止事件</param>
        /// <returns>状态构建器</returns>
        public StateBuilder<T> OnTermination(Action<T> onTermination)
        {
            state.onTermination = () => onTermination(state);
            return this;
        }
        /// <summary>
        /// 设置状态切换条件
        /// </summary>
        /// <param name="predicate">切换条件</param>
        /// <param name="targetStateName">目标状态名称</param>
        /// <returns>状态构建器</returns>
        public StateBuilder<T> SwitchWhen(Func<bool> predicate, string targetStateName)
        {
            state.SwitchWhen(predicate, targetStateName);
            return this;
        }
        /// <summary>
        /// 构建完成
        /// </summary>
        /// <returns>状态机</returns>
        public StateMachine Complete()
        {
            state.OnInitialization();
            return stateMachine;
        }
    }
}

状态机:

csharp 复制代码
using FSM;
using System;
using System.Collections.Generic;

namespace FSM
{
    /// <summary>
    /// 状态机
    /// </summary>
    public class StateMachine
    {
        //状态列表 存储状态机内所有状态
        private readonly List<State> states = new List<State>();
        //状态切换条件列表
        private readonly List<StateSwitchCondition> conditions = new List<StateSwitchCondition>();

        /// <summary>
        /// 状态机名称
        /// </summary>
        public string Name { get; internal set; }
        /// <summary>
        /// 当前状态
        /// </summary>
        public State CurrentState { get; protected set; }

        /// <summary>
        /// 状态机初始化事件
        /// </summary>
        public virtual void OnInitialization() { }

        /// <summary>
        /// 添加状态
        /// </summary>
        /// <param name="state">状态</param>
        /// <returns>0:添加成功; -1:状态已存在,无需重复添加; -2:存在同名状态,添加失败</returns>
        public int Add(State state)
        {
            //判断是否已经存在
            if (!states.Contains(state))
            {
                //判断是否存在同名状态
                if (states.Find(m => m.Name == state.Name) == null)
                {
                    //存储到列表
                    states.Add(state);
                    //执行状态初始化事件
                    state.OnInitialization();
                    //设置状态所属的状态机
                    state.Machine = this;
                    return 0;
                }
                return -2;
            }
            return -1;
        }
        /// <summary>
        /// 添加状态
        /// </summary>
        /// <typeparam name="T">状态类型</typeparam>
        /// <param name="stateName">状态命名</param>
        /// <returns>0:添加成功; -1:状态已存在,无需重复添加; -2:存在同名状态,添加失败</returns>
        public int Add<T>(string stateName = null) where T : State, new()
        {
            Type type = typeof(T);
            T t = (T)Activator.CreateInstance(type);
            t.Name = string.IsNullOrEmpty(stateName) ? type.Name : stateName;
            return Add(t);
        }

        /// <summary>
        /// 移除状态
        /// </summary>
        /// <param name="stateName">状态名称</param>
        /// <returns>true:移除成功; false:状态不存在,移除失败</returns>
        public bool Remove(string stateName)
        {
            //根据状态名称查找目标状态
            var target = states.Find(m => m.Name == stateName);
            if (target != null)
            {
                //如果要移除的状态为当前状态 首先执行当前状态退出事件
                if (CurrentState == target)
                {
                    CurrentState.OnExit();
                    CurrentState = null;
                }
                //执行状态终止事件
                target.OnTermination();
                //从列表中移除
                states.Remove(target);
                return true;
            }
            return false;
        }
        /// <summary>
        /// 移除状态
        /// </summary>
        /// <param name="state">状态</param>
        /// <returns>true:移除成功; false:状态不存在,移除失败</returns>
        public bool Remove(State state)
        {
            return Remove(state.Name);
        }
        /// <summary>
        /// 移除状态
        /// </summary>
        /// <typeparam name="T">状态类型</typeparam>
        /// <returns>true:移除成功; false:状态不存在,移除失败</returns>
        public bool Remove<T>() where T : State
        {
            return Remove(typeof(T).Name);
        }

        /// <summary>
        /// 切换状态
        /// </summary>
        /// <param name="stateName">状态名称</param>
        /// <param name="data">数据</param>
        /// <returns>0:切换成功; -1:状态不存在; -2:当前状态已经是切换的目标状态,并且该状态不可切换至自身</returns>
        public int Switch(string stateName, IStateData data = null)
        {
            //根据状态名称在列表中查询
            var target = states.Find(m => m.Name == stateName);
            if (target == null) return -1;
            //如果当前状态已经是切换的目标状态 并且该状态不可切换至自身 无需切换 返回false
            if (CurrentState == target && !target.CanSwitch2Self) return -2;
            //当前状态不为空则执行状态退出事件
            CurrentState?.OnExit();
            //更新当前状态
            CurrentState = target;
            //更新后 执行状态进入事件
            CurrentState.OnEnter(data);
            return 0;
        }
        /// <summary>
        /// 切换状态
        /// </summary>
        /// <param name="state">状态</param>
        /// <returns>0:切换成功; -1:状态不存在; -2:当前状态已经是切换的目标状态,并且该状态不可切换至自身</returns>
        public int Switch(State state)
        {
            return Switch(state.Name);
        }
        /// <summary>
        /// 切换状态
        /// </summary>
        /// <typeparam name="T">状态类型</typeparam>
        /// <typeparam name="data">数据</typeparam>
        /// <returns>0:切换成功; -1:状态不存在; -2:当前状态已经是切换的目标状态,并且该状态不可切换至自身</returns>
        public int Switch<T>(IStateData data = null) where T : State
        {
            return Switch(typeof(T).Name, data);
        }

        /// <summary>
        /// 切换至下一状态
        /// </summary>
        /// <returns>true:切换成功; false:状态机中不存在任何状态,切换失败</returns>
        public bool Switch2Next()
        {
            if (states.Count != 0)
            {
                //如果当前状态不为空 则根据当前状态找到下一个状态
                if (CurrentState != null)
                {
                    int index = states.IndexOf(CurrentState);
                    //当前状态的索引值+1后若小于列表中的数量 则下一状态的索引为index+1
                    //否则表示当前状态已经是列表中的最后一个 下一状态则回到列表中的第一个状态 索引为0
                    index = index + 1 < states.Count ? index + 1 : 0;
                    State targetState = states[index];
                    //首先执行当前状态的退出事件 再更新到目标状态
                    CurrentState.OnExit();
                    CurrentState = targetState;
                }
                //当前状态为空 则直接进入列表中的第一个状态
                else
                {
                    CurrentState = states[0];
                }
                //执行状态进入事件
                CurrentState.OnEnter();
                return true;
            }
            return false;
        }
        /// <summary>
        /// 切换至上一状态
        /// </summary>
        /// <returns>true:切换成功; false:状态机中不存在任何状态,切换失败</returns>
        public bool Switch2Last()
        {
            if (states.Count != 0)
            {
                //如果当前状态不为空 则根据当前状态找到上一个状态
                if (CurrentState != null)
                {
                    int index = states.IndexOf(CurrentState);
                    //当前状态的索引值-1后若大等于0 则下一状态的索引为index-1
                    //否则表示当前状态是列表中的第一个 上一状态则回到列表中的最后一个状态
                    index = index - 1 >= 0 ? index - 1 : states.Count - 1;
                    State targetState = states[index];
                    //首先执行当前状态的退出事件 再更新到目标状态
                    CurrentState.OnExit();
                    CurrentState = targetState;
                }
                //当前状态为空 则直接进入列表中的最后一个状态
                else
                {
                    CurrentState = states[states.Count - 1];
                }
                //执行状态进入事件
                CurrentState.OnEnter();
                return true;
            }
            return false;
        }
        /// <summary>
        /// 切换至空状态(退出当前状态)
        /// </summary>
        public void Switch2Null()
        {
            if (CurrentState != null)
            {
                CurrentState.OnExit();
                CurrentState = null;
            }
        }

        /// <summary>
        /// 获取状态
        /// </summary>
        /// <typeparam name="T">状态类型</typeparam>
        /// <param name="stateName">状态名称</param>
        /// <returns>状态</returns>
        public T GetState<T>(string stateName) where T : State
        {
            var target = states.Find(m => m.Name == stateName);
            return target != null ? target as T : null;
        }
        /// <summary>
        /// 获取状态
        /// </summary>
        /// <typeparam name="T">状态类型</typeparam>
        /// <returns>状态</returns>
        public T GetState<T>() where T : State
        {
            return GetState<T>(typeof(T).Name);
        }

        /// <summary>
        /// 销毁状态机
        /// </summary>
        public void Destroy()
        {
            //Main.FSM.Destroy(this);
        }
        /// <summary>
        /// 状态机刷新事件
        /// </summary>
        internal void OnUpdate()
        {
            //若当前状态不为空 执行状态停留事件
            CurrentState?.OnStay();
            //检测所有状态切换条件
            for (int i = 0; i < conditions.Count; i++)
            {
                var condition = conditions[i];
                //条件满足
                if (condition.predicate.Invoke())
                {
                    //源状态名称为空 表示从任意状态切换至目标状态
                    if (string.IsNullOrEmpty(condition.sourceStateName))
                    {
                        Switch(condition.targetStateName);
                    }
                    //源状态名称不为空 表示从指定状态切换至目标状态
                    else
                    {
                        //首先判断当前的状态是否为指定的状态
                        if (CurrentState.Name == condition.sourceStateName)
                        {
                            Switch(condition.targetStateName);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// 状态机销毁事件
        /// </summary>
        internal void OnDestroy()
        {
            //执行状态机内所有状态的状态终止事件
            for (int i = 0; i < states.Count; i++)
            {
                states[i].OnTermination();
            }
        }

        /// <summary>
        /// 构建状态
        /// </summary>
        /// <typeparam name="T">状态类型</typeparam>
        /// <param name="stateName">状态名称</param>
        /// <returns>状态构建器</returns>
        public StateBuilder<T> Build<T>(string stateName = null) where T : State, new()
        {
            Type type = typeof(T);
            string name = string.IsNullOrEmpty(stateName) ? type.Name : stateName;
            if (states.Find(m => m.Name == name) == null)
            {
                T state = Activator.CreateInstance(type) as T;
                state.Name = name;
                state.Machine = this;
                states.Add(state);
                return new StateBuilder<T>(state, this);
            }
            return null;
        }
        /// <summary>
        /// 设置状态切换条件
        /// </summary>
        /// <param name="predicate">切换条件</param>
        /// <param name="targetStateName">目标状态名称</param>
        /// <returns>状态机</returns>
        public StateMachine SwitchWhen(Func<bool> predicate, string targetStateName)
        {
            conditions.Add(new StateSwitchCondition(predicate, null, targetStateName));
            return this;
        }
        /// <summary>
        /// 设置状态切换条件
        /// </summary>
        /// <param name="predicate">切换条件</param>
        /// <param name="sourceStateName">源状态名称</param>
        /// <param name="targetStateName">目标状态名称</param>
        /// <returns></returns>
        public StateMachine SwitchWhen(Func<bool> predicate, string sourceStateName, string targetStateName)
        {
            conditions.Add(new StateSwitchCondition(predicate, sourceStateName, targetStateName));
            return this;
        }
    }
}

通过Add函数去添加状态,除此之外,可以通过Build构建状态实现添加,用于链式编程:

csharp 复制代码
public StateBuilder<T> Build<T>(string stateName = null) where T : State, new()

设置状态切换条件:

csharp 复制代码
public StateMachine SwitchWhen(Func<bool> predicate, string sourceStateName, string targetStateName)

三、后记

如果觉得本篇文章有用别忘了点个关注,关注不迷路,持续分享更多Unity干货文章。


你的点赞就是对博主的支持,有问题记得留言:

博主主页有联系方式。

博主还有跟多宝藏文章等待你的发掘哦:

专栏 方向 简介
GameFramework框架 框架 Unity之GameFramework框架快速应用、使用说明、源码分析等文章合集。
Unity3D开发小游戏 小游戏开发教程 分享一些使用Unity3D引擎开发的小游戏,分享一些制作小游戏的教程。
Unity3D从入门到进阶 入门 从自学Unity中获取灵感,总结从零开始学习Unity的路线,有C#和Unity的知识。
Unity3D之UGUI UGUI Unity的UI系统UGUI全解析,从UGUI的基础控件开始讲起,然后将UGUI的原理,UGUI的使用全面教学。
Unity3D之读取数据 文件读取 使用Unity3D读取txt文档、json文档、xml文档、csv文档、Excel文档。
Unity3D之数据集合 数据集合 数组集合:数组、List、字典、堆栈、链表等数据集合知识分享。
Unity3D之VR/AR(虚拟仿真)开发 虚拟仿真 总结博主工作常见的虚拟仿真需求进行案例讲解。
Unity3D之插件 插件 主要分享在Unity开发中用到的一些插件使用方法,插件介绍等
Unity3D之日常开发 日常记录 主要是博主日常开发中用到的,用到的方法技巧,开发思路,代码分享等
Unity3D之日常BUG 日常记录 记录在使用Unity3D编辑器开发项目过程中,遇到的BUG和坑,让后来人可以有些参考。
相关推荐
学习嵌入式的小羊~1 小时前
RV1126+FFMPEG推流项目(11)编码音视频数据 + FFMPEG时间戳处理
ffmpeg·音视频
Yungoal2 小时前
Unity入门1
unity·游戏引擎
CASAIM3 小时前
手持式三维激光扫描仪-3D扫描产品尺寸
3d·信息可视化
刘大猫.4 小时前
vue3使用音频audio标签
音视频·audio·preload·加载音频文件·vue3使用audio·vue3使用音频·audio标签
qq_4286396110 小时前
虚幻基础1:hello world
游戏引擎·虚幻
虾球xz12 小时前
游戏引擎学习第84天
学习·游戏引擎
杀死一只知更鸟debug16 小时前
Unity自学之旅04
unity
神洛华16 小时前
Y3编辑器2.0功能指引
编辑器
k56946216617 小时前
失业ing
unity·游戏引擎
优联前端17 小时前
Web 音视频(二)在浏览器中解析视频
前端·javascript·音视频·优联前端·webav