美文网首页
[个人框架 C_Framework] FSM状态机

[个人框架 C_Framework] FSM状态机

作者: hh5460 | 来源:发表于2018-01-18 18:16 被阅读0次

    该状态机逻辑

    该状态机逻辑.png 按下启动O状态.png 按下启动M状态,A Stay开始执行.png 按下启动N状态,A Stay持续执行.png

    测试代码

    using UnityEngine;
    
    public class C_FSM_Test: MonoBehaviour
    {
        C_FSMCtrl fsmC;
        C_FSMState stateA;     //也可以通过 fsmC.dicALLStates["StateA"] 来获取,此处演示,直接拿到stateA实例
        // Use this for initialization
        void Start()
        {
            fsmC = new C_FSMCtrl(this.name);
            stateA = fsmC.AddState("StateA", () => { Debug.Log("A Start!!!"); }, () => { Debug.Log("A Over!!!"); }, () => { Debug.Log("A Stay Continue Print"); });
    
            fsmC.AddState("StateB", () => { Debug.Log("B Start!!!"); }, () => { Debug.Log("B Over!!!"); });
            fsmC.AddState("StateC", () => { Debug.Log("C Start!!!"); }, () => { Debug.Log("C Over!!!"); });
    
            //通过指定父状态C_FSMState对象的方式添加当前状态到stateA状态
            fsmC.AddState("StateD", () => { Debug.Log("E Start!!!"); }, () => { Debug.Log("E Over!!!"); },null, stateA);
            fsmC.AddState("StateE", () => { Debug.Log("F Start!!!"); }, () => { Debug.Log("F Over!!!"); },null, stateA);
            fsmC.AddState("StateF", () => { Debug.Log("G Start!!!"); }, () => { Debug.Log("G Over!!!"); },null, stateA);
    
            //通过指定父状态字符串的方式添加状态
            fsmC.AddState("StateG", "StateB", () => { Debug.Log("G Start!!!"); }, () => { Debug.Log("G Over!!!"); });
            fsmC.AddState("StateH", "StateC", () => { Debug.Log("H Start!!!"); }, () => { Debug.Log("H Over!!!"); });
            fsmC.AddState("StateI", "StateC", () => { Debug.Log("I Start!!!"); }, () => { Debug.Log("I Over!!!"); });
            fsmC.AddState("StateJ", "StateD", () => { Debug.Log("J Start!!!"); }, () => { Debug.Log("J Over!!!"); });
            fsmC.AddState("StateK", "StateD", () => { Debug.Log("K Start!!!"); }, () => { Debug.Log("K Over!!!"); });
            fsmC.AddState("StateO", "StateH", () => { Debug.Log("O Start!!!"); }, () => { Debug.Log("O Over!!!"); });
            fsmC.AddState("StateL", "StateJ", () => { Debug.Log("L Start!!!"); }, () => { Debug.Log("L Over!!!"); });
            fsmC.AddState("StateM", "StateK", () => { Debug.Log("M Start!!!"); }, () => { Debug.Log("M Over!!!"); });
            fsmC.AddState("StateN", "StateK", () => { Debug.Log("N Start!!!"); }, () => { Debug.Log("N Over!!!"); });
        }
    
        // Update is called once per frame
        void Update()
        {
            //为什么在此处调用,如果在FSMState自身调用,那么所有的状态都会调用,即使它没有任何实质函数内容
            //但放在此处可以按需调用,也可以放在FixedUpdate中调用,或者定时调用,更加灵活
            stateA.stay();
    
            if (Input.GetKeyDown(KeyCode.O))
            {
                Debug.Log("按下启动O状态");
                fsmC.StartState("StateO");
            }
    
            if (Input.GetKeyDown(KeyCode.M))
            {
                Debug.Log("按下启动M状态");
                fsmC.StartState("StateM");
            }
    
            if (Input.GetKeyDown(KeyCode.N))
            {
                Debug.Log("按下启动N状态");
                fsmC.StartState("StateN");
            }     
        }
    }
    
    using System;
    using System.Collections.Generic;
    
    /// <summary>
    /// FSM状态
    /// </summary>
    public class C_FSMState
    {
        //当前状态是否处于激活状态
        public bool isActivity;
    
        //当前状态的路径,路径记录了父类状态名称 例如 ROOT/StateA/StateB/StateNow
        public string path;
    
        //记录当前状态开始时的委托
        public Action act_start;
    
        //记录当前状态结束时的委托
        public Action act_over;
    
        //记录当状态stay驻留时候的委托,这个方法可以放在绑定物体的update()函数中重复执行
        public Action act_stay;
    
        //记录当前状态所属于的状态控制器
        public C_FSMCtrl fsmCtrl;           //控制器
    
        //当前状态名称
        public string StateName;
    
        //当前状态的所属的父状态
        public C_FSMState StateFather;
    
        //当前状态的子状态容器
        public Dictionary<string, C_FSMState> dicChilds;              //子节点
    
        /// <summary>
        /// 仅包含一个名称的状态的构造函数
        /// </summary>
        /// <param name="stateName">状态名</param>
        public C_FSMState(string stateName)
        {
            StateName = stateName;
            path = StateName;
        }
    
        /// <summary>
        /// 一个状态的构造函数
        /// </summary>
        /// <param name="stateName">状态名</param>
        /// <param name="_act_start">当前状态开始时的委托</param>
        /// <param name="_act_over">当前状态关闭时的委托</param>
        public C_FSMState(string stateName, Action _act_start = null, Action _act_over = null, Action _act_stay = null)
        {
            StateName = stateName;
            act_start = _act_start;
            act_over = _act_over;
            act_stay = _act_stay;
            path = StateName;
        }
    
        /// <summary>
        /// 开始一个状态
        /// </summary>
        public void start()
        {
            //若当前状态已经激活
            if (isActivity == true)
            {
                return;
            }
            //如果当前状态的父状态不为空,切父状态未开启,则先开启父状态
            if (this.StateFather != null && this.StateFather.isActivity == false)
            {
                this.StateFather.start();
            }
            
            //将当前状态至于开启状态
            isActivity = true;
            
            //设置当前状态控制器为当前状态为此状态
            fsmCtrl.currentState = this;
    
            //如果设置了开启状态的回调函数,则进行回调
            if (act_start != null)
            {
                act_start();
            }
        }
    
        /// <summary>
        /// 记录当状态stay驻留时候的委托,这个方法可以放在绑定物体的update()函数中重复执行
        /// </summary>
        public void stay()
        {
            if (isActivity && act_stay!=null)
            {
                act_stay();
            }
        }
    
        /// <summary>
        /// 状态结束
        /// </summary>
        public void over()
        {
            //如果当前状态已经处于结束状态则返回
            if (isActivity == false)
            {
                return;
            }
    
            //如果当前状态存在子状态
            if (this.dicChilds != null)
            {
                //则逐级关闭当前状态
                var emut = dicChilds.GetEnumerator();
                while (emut.MoveNext())
                {
                    emut.Current.Value.over();
                }
            }
            //将当前状态激活状态关闭
            isActivity = false;
    
            //如果状态控制器的 当前状态为自己,则关闭自己后将其设置为父状态
            if (fsmCtrl.currentState != null && fsmCtrl.currentState == this)
            {
                fsmCtrl.currentState = StateFather;
            }
            //如果当前状态的关闭委托不为空,则调用当前委托
            if (act_over != null)
            {
                act_over();
            }
        }
    }
    
    using System;
    using System.Collections.Generic;
    
    public class C_FSMCtrl
    {
        private string rootStateName = "root";                  //根状态名
        public C_FSMState rootState;                            //根状态
        public C_FSMState currentState;                         //当前状态
        public string ctrlName;                                 //状态控制器名称
        public Dictionary<string, C_FSMState> dicALLStates;     //该控制器下的所有状态机
    
        /// <summary>
        /// 构造一个状态机控制器
        /// </summary>
        /// <param name="name">当前状态机控制器名称</param>
        public C_FSMCtrl(string name)
        {
            //当前状态机控制器名称
            ctrlName = name;
            //实例化一个状态字典,用于存储所有的子状态
            dicALLStates = new Dictionary<string, C_FSMState>();
            //实例化一个根状态
            rootState = new C_FSMState(rootStateName);
            //如果所有的状态中不包含当前的根状态,则将其加入当所有状态中
            if (!dicALLStates.ContainsKey(name))
            {
                //设置根状态的控制器为自身
                rootState.fsmCtrl = this;
                //将根状态加入到当前控制器状态集合中
                dicALLStates.Add(name, rootState);
            }
            //将当前状态控制器加入到 所有状态控制 背包,该背包存储所有的状态控制器
            C_FSMCtrlBags.AddFsmc(this);
        }
    
        /// <summary>
        /// 为当前的状态控制器增加一个状态
        /// </summary>
        /// <param name="stateName">状态名称</param>
        /// <param name="_act_start">状态开始时候的回调函数</param>
        /// <param name="_act_over">状态结束时的回掉函数</param>
        /// <param name="C_FSMState">返回增加的状态</param>
        public C_FSMState AddState(string stateName,Action _act_start = null, Action _act_over = null, Action _act_stay = null,C_FSMState parentState = null)
        {
            //新建一个子状态
            C_FSMState childState = null;
            //如果当前状态控制器已经包含该子状态则自己取出该状态
            if (dicALLStates.ContainsKey(stateName))
            {
                childState = dicALLStates[stateName];
            }
            else
            {
                //如果当前控制器不包含该子状态,则新建该状态
                childState = new C_FSMState(stateName);
            }
    
            //更新它绑定的三个委托
            childState.act_start = _act_start;
            childState.act_over = _act_over;
            childState.act_stay = _act_stay;
    
            //若没有设置父节点,则默认为rootState节点
            if (parentState == null)
            {
                parentState = rootState;
            }
    
            //如果当前状态的父状态存储子类状态的字典不存在
            if (parentState.dicChilds == null)
            {
                //则实例化子类状态存储的容器
                parentState.dicChilds = new Dictionary<string, C_FSMState>();
            }
    
            //如果当前状态的父状态不等于null则表明当前已经存在父状态,那么就相当于是要切换父状态,需将原先的父状态删除
            if (childState.StateFather != null) // && childState.StateFather.dicChilds != null && childState.StateFather.dicChilds.ContainsKey(stateName))
            {
                childState.StateFather.dicChilds.Remove(stateName);
            }
    
            //指定当前状态的父状态
            childState.StateFather = parentState;
            //将当前状态的加入到父状态的子状态字典中
            parentState.dicChilds.Add(childState.StateName, childState);
            //设置子状态的路径
            childState.path = parentState.path + "/" + childState.StateName;
    
            //如果当前状态控制器不包含新状态的名称,则表示是新建状态,需要添加到字典中
            //否则则表示新状态已经在控制器中了,说明是修改状态,修改后无需重复添加
            if (!dicALLStates.ContainsKey(stateName))
            {
                //指定当前控制器为自身
                childState.fsmCtrl = this;
                //将新生成的状态追加到状态控制器字典中
                dicALLStates.Add(stateName, childState);
            }
            return childState;
        }
    
        /// <summary>
        /// 为当前的状态控制器增加一个状态
        /// </summary>
        /// <param name="stateName">状态名称</param>
        /// <param name="parentStateName">指定父状态名称</param>
        /// <param name="_act_start">状态开始时候的回调函数</param>
        /// <param name="_act_over">状态结束时的回掉函数</param>
        /// <param name="C_FSMState">返回增加的状态</param>
        public C_FSMState AddState(string stateName, string parentStateName, Action _act_start = null, Action _act_over = null,Action _act_stay=null)
        {
            C_FSMState parentState = rootState;
            //如果指定的当前父状态名称不等于默认的根状态名称,则表示当前的状态并不是要设置在根状态下
            if (parentStateName != rootStateName)
            {
                //从当前状态机中取出需要设置的根状态
                if (dicALLStates.ContainsKey(parentStateName))
                {
                    parentState = dicALLStates[parentStateName];
                }
                else
                {
                    //如果不存在则抛出异常,指定当前状态的父状态不存在
                    throw new System.Exception("名为:" + ctrlName + "的状态控制器不存在" + parentStateName + "索引");
                }
            }
            return AddState(stateName, _act_start, _act_over, _act_stay, parentState);
        }
    
        //移除状态机,但是不销毁
        public void RemoveState(string stateName)
        {
            //如果当前状态控制器中包含指定名称的状态
            if (dicALLStates.ContainsKey(stateName))
            {
                //取出当前状态
                C_FSMState state = dicALLStates[stateName];
                //如果当前状态是激活状态,则调用over终止状态
                if (state.isActivity)
                {
                    state.over();
                }
                //递归回收该状态和所有子状态
                RecursionStop(state);
            }
        }
    
        //开始一个状态
        public void StartState(string stateName)
        {
            //如果当前控制器中包含指定名称的状态
            if (dicALLStates.ContainsKey(stateName))
            {
                //需要开始的状态
                C_FSMState nextState = dicALLStates[stateName];
                if (currentState != null)     //如果控制器当前的状态不为空,则表示当前处于一个状态中,说明是切换状态
                {
                    //需要开始的状态的父状态不为空,而且当前状态的父状态和需要开始的父状态相同,则表示同父是亲兄弟状态
                    if (nextState.StateFather != null && nextState.StateFather == currentState.StateFather)
                    {
                        //亲兄弟
                        //表示是兄弟状态允许切换,先终止当前状态,然后开始另外的状态
                        currentState.over();
                        nextState.start();
                    }
                    else //表示他们的父状态不同
                    {
                        /*
                                          ROOT
                                   |     |     |     |
                                   A     B     C     D
                                 |   |   |   |   |  | |
                                 E   F   G   H   I  J K
                         
                         假设当前状态是E,需要切换到H
                         1.找到他们相同的祖先(此处例子为ROOT),当前激活状态向上递归over()直到相同祖先(不包含相同祖先),切换后的状态递归start()直到相同祖先(不包含相同祖先),即
                         2.I状态over()
                         3.C状态over()
                         4.A状态start()
                         5.E状态start()
                                                        
                         */
                        //非亲兄弟, 则需要先回溯找到共同的父亲节点,然后切换,当前状态逐级向上调用over()  
                        //1.找到公共父类节点
                        string[] arrPath1 = currentState.path.Split('/');
                        string[] arrPath2 = nextState.path.Split('/');
                        //防止索引越界,根据索引小遍历
                        int length = Math.Min(arrPath1.Length, arrPath2.Length);
                        //共同父状态名称
                        string togetherState = "";
                        //路径从根往下对比
                        //ROOT/A/E
                        //ROOT/C/I
                        //取得相同路径ROOT即为共同祖先
                        for (int i = 0; i < length; i++)
                        {
                            if (arrPath1[i] != arrPath2[i])
                            {
                                break;
                            }
                            else
                            {
                                togetherState = arrPath1[i];
                            }
                        }
                        /*由于FSMState中已经默认使用了递归开启和关闭,所以下面递归关闭就多余了,直接关闭和打开对应的节点即可
                        //将当前状态从下级往上级逐级关闭
                        for (int i = arrPath1.Length - 1; i >= 0; i--)
                        {
                            if (arrPath1[i] == togetherState)
                            {
                                break;
                            }
                            dicALLStates[arrPath1[i]].over();
                        }
    
                        //将需要新打开的状态从上级往下级逐级开启
                        bool isStart = false;
                        for (int i = 0; i < arrPath2.Length; i++)
                        {
                            if (togetherState == arrPath2[i])
                            {
                                isStart = true;
                                continue;
                            }
    
                            if (isStart)
                            {
                                dicALLStates[arrPath2[i]].start();
                            }
                        }
                        */
    
                        //ROOT/C/I 之需要关闭C状态即可,默认会自动递归关闭I状态
                        bool selectNext = false;
                        for (int i = 0; i < arrPath1.Length; i++)
                        {
                            if (togetherState == arrPath1[i])
                            {
                                selectNext = true;
                                continue;
                            }
    
                            if (selectNext)
                            {
                                dicALLStates[arrPath1[i]].over();
                                break;
                            }
                        }
    
                        //ROOT/A/E 直接开启E状态即可,默认会自动递归开启A状态
                        dicALLStates[arrPath2[arrPath2.Length - 1]].start();
                    }
                }
                else //如果当前状态机并没有开启任何状态,则开始当前状态
                {
                    nextState.start();
                }
                ////将当前状态机指向新开启的状态机,无需指定,start()函数中,默认会绑定
                //currentState = nextState;
            }
            else
            {
                //当需要开启的状态机不存在时
                throw new Exception(ctrlName + "状态机字典中不存在对应的的状态" + stateName);
            }
        }
    
        //停止状态
        public void StopState(string stateName)
        {
            //如果当前状态存在,则关闭,默认会递归调用子类的状态进行关闭
            if (dicALLStates.ContainsKey(stateName))
            {
                dicALLStates[stateName].over();
            }
        }
    
        //直接递归销毁子类状态并且移除,不调用over
        void RecursionStop(C_FSMState state)
        {
            //如果传入的状态的子状态不为空
            if (state.dicChilds != null)
            {
                //则获取所有的子状态,仅包括子,不包括孙或更下一辈
                List<C_FSMState> lstChilds = new List<C_FSMState>();
                foreach (var item in state.dicChilds)
                {
                    lstChilds.Add(item.Value);
                }
    
                //递归调用子类的
                for (int i = 0; i < lstChilds.Count; i++)
                {
                    RecursionStop(lstChilds[i]);
                }
            }
    
            //如果当前状态存在父状态,则将当前状态从父状态中移除
            if (state.StateFather != null)
            {
                state.StateFather.dicChilds.Remove(state.StateName);
            }
    
            //如果控制器中包含当前状态,则将当前状态从控制器中移除
            if (dicALLStates.ContainsKey(state.StateName))
            {
                dicALLStates.Remove(state.StateName);
            }
    
            //清楚当前的状态的子字典,路径,父状态,父名称
            state.dicChilds = null;
            state.path = null;
            state.StateFather = null;
            state.StateName = "";
        }
    }
    
    using System.Collections.Generic;
    
    public class C_FSMCtrlBags{
    
        //存储所有状态控制器的Bags
        static Dictionary<string, C_FSMCtrl> dicFsmcs = new Dictionary<string, C_FSMCtrl>();
    
        //根据字符串索引状态控制器
        public C_FSMCtrl this[string indexStr]
        {
            get
            {
                //如果存在则返回,如果不存在则抛出异常
                C_FSMCtrl res = null;
                if (dicFsmcs.ContainsKey(indexStr))
                {
                    res = dicFsmcs[indexStr];
                }
                else
                {
                    throw new System.Exception("FSMCtrlBags不存在指定的索引"+ indexStr);
                }
                return res;
            }
        }
    
        //将控制器加入到控制器背包中
        public static void AddFsmc(C_FSMCtrl fsmc)
        {
            if (!dicFsmcs.ContainsKey(fsmc.ctrlName))
            {
                dicFsmcs.Add(fsmc.ctrlName, fsmc);
            }
            else
            {
                throw new System.Exception("FSMCtrlBags已存在key:" + fsmc.ctrlName);
            }
        }
    }
    
    

    相关文章

      网友评论

          本文标题:[个人框架 C_Framework] FSM状态机

          本文链接:https://www.haomeiwen.com/subject/aabwoxtx.html