美文网首页
状态机_fsm(Finite State Machine)

状态机_fsm(Finite State Machine)

作者: _谭小坤 | 来源:发表于2018-02-02 18:41 被阅读25次

    一.状态机


    状态机

    二.个人理解的状态机


    个人理解的状态机.jpg

    三.自己搭建一个状态机


    搭建状态机步骤
    状态机,管理多个状态,每个状态有多个动作,状态下动作的改变切换到另一个状态
    1.创建状态机的管理(Manager)
    using UnityEngine;
    using System.Collections;
    
    namespace SpacePlanner
    {
        /// <summary>
        /// 处理用户的一些复杂输入,并不是全部
        /// 尤其是那些一步一步进行的操作
        /// </summary>
        public class UserInputHandler : MonoBehaviour
        {
            public bool IsRunning
            {
                get
                {
                    return fsm != null;
                }
            }
    
            static UserInputHandler _instance;
            public static UserInputHandler Instance
            {
                get
                {
                    if (_instance == null)
                    {
                        GameObject go = new GameObject("UserInputHandler");
                        _instance = go.AddComponent<UserInputHandler>();
                    }
                    return _instance;
                }
            }
            
            FSM4InputHandler fsm;       
            void Update()//状态机的执行入口
            {
                if (fsm != null)
                    fsm.Run();
            }
    
            public void StartHandleBuildWallInput()
            {
                fsm = new FSM4InputHandler();
    
    
                var tmpState = new State(fsm, "StartState");
                tmpState.AddAction(new MouseClickToStart("InBuildWallState"));
                tmpState.AddAction(new MouseMoveBeforeBuild());
                tmpState.AddAction(new MouseRightClickToStop());
                fsm.AddState(tmpState);
                fsm.curState = tmpState;
    
                tmpState = new State(fsm, "InBuildWallState");
                tmpState.AddAction(new MouseRightClickToStop());
                tmpState.AddAction(new MouseClickToContinue("StartState"));
                tmpState.AddAction(new MouseMoveInBuild());
                fsm.AddState(tmpState);
    
            }
    
       //     public void StartHandleBuildWallInput()
       //     {
       //         fsm = new FSM4InputHandler();
              
       //         var tmpState = new State(fsm, "StartState");
       //         tmpState.AddAction(new MouseClickToStart("InBuildWallState"));
                //tmpState.AddAction(new MouseMoveBeforeBuild());
       //         tmpState.AddAction(new MouseRightClickToStop());
       //         fsm.AddState(tmpState);
       //         fsm.curState = tmpState;
    
       //         tmpState = new State(fsm, "InBuildWallState");
       //         tmpState.AddAction(new MouseRightClickToStop());
       //         tmpState.AddAction(new MouseClickToContinue());
       //         tmpState.AddAction(new MouseMoveInBuild());
       //         fsm.AddState(tmpState);
               
       //     }
    
            public void StartHandleBuildAreaInput()
            {
                fsm = new FSM4InputHandler();
                //var tmpState = new State(fsm, "StartState");
                //tmpState.AddAction(new MoveBeforBuildArea());
                //tmpState.AddAction(new ClickToStartBuildArea("InBuildAreaState"));
                //fsm.AddState(tmpState);
                //fsm.curState = tmpState;
    
                var tmpState = new State(fsm, "InBuildAreaState");
                tmpState.AddAction(new MoveInBuildArea());
                tmpState.AddAction(new ClickToContinueBuildArea());
                tmpState.AddAction(new RightClickToStopBuildArea());
                fsm.AddState(tmpState);
                fsm.curState = tmpState;
            }
    
            public void StartHandleBuildDoorInput()
            {
                fsm = new FSM4InputHandler();
    
                var tmpState = new State(fsm, "StartState");
                tmpState.AddAction(new MouseMoveBeforeAddDoor());
                tmpState.AddAction(new MouseClickToAddDoor());
                tmpState.AddAction(new MouseRightClickToStopAddDoor());
                fsm.AddState(tmpState);
                fsm.curState = tmpState;
            }
    
    
            /// <summary>
            /// Alan添加的建立户型状态机控制
            /// </summary>
            public void StartHandleBuildUnitInput()
            {
                fsm = new FSM4InputHandler();
    
                var tmpState = new State(fsm, "StartState");
                tmpState.AddAction(new MouseMoveBeforeAddUnit());
                tmpState.AddAction(new MouseClickToAddUnit());
                tmpState.AddAction(new MouseRightClickToStopAddUnit());
                fsm.AddState(tmpState);
                fsm.curState = tmpState;
            }
    
            public void StartHandleBuildWinInput()
            {
                fsm = new FSM4InputHandler();
                var tmpState = new State(fsm, "StartState");
                tmpState.AddAction(new MouseMoveBeforeBuildWin());
                tmpState.AddAction(new MouseClickToBuildWin());
                tmpState.AddAction(new MouseRightClickToStopBuildWindow());
                fsm.AddState(tmpState);
                fsm.curState = tmpState;
            }
    
            //public void StartHandleBuildHandrailInput()
            //{
            //  fsm = new FSM4InputHandler();
            //  var tmpState = new State(fsm, "StartState");
            //  tmpState.AddAction(new MouseMoveBeforeAddHandrail());
            //  tmpState.AddAction(new MouseClickToAddHandrail());
            //  tmpState.AddAction(new MouseRightClickToStopAddHandrail());
            //  fsm.AddState(tmpState);
            //  fsm.curState = tmpState;
            //}
    
            public void StartHandleBuildPillarInput()
            {
                fsm = new FSM4InputHandler();
                var tmpState = new State(fsm, "StartState");
                tmpState.AddAction(new MouseMoveBeforeAddSpaceObject());
                tmpState.AddAction(new MouseClickToAddSpaceObject());
                tmpState.AddAction(new MouseRightClickToStopAddSpaceObject());
                fsm.AddState(tmpState);
                fsm.curState = tmpState;
            }
    
            public void StartSetApartmentMapMeasureInput()
            {
                fsm = new FSM4InputHandler();
    
                var tmpState = new State(fsm, "StartState");
                tmpState.AddAction(new MouseClickToStartSetMeasure("InSetApartmentMapMeasureState"));
                tmpState.AddAction(new MouseRightClickToStopSetMeasure());
                fsm.AddState(tmpState);
                fsm.curState = tmpState;
    
                tmpState = new State(fsm, "InSetApartmentMapMeasureState");
                tmpState.AddAction(new MouseClickToFinishSetMeasure());
                tmpState.AddAction(new MouseRightClickToStopSetMeasure());
                tmpState.AddAction(new MouseMoveInSetMeasure());
                fsm.AddState(tmpState);
            }
           具体分析这个状态机的例子
           1.2个状态"StartState"状态,"InBuildWallState"状态
           2"StartState"状态的三个动作MouseClickToStartMeasure,MouseMoveBeforeMeasure,MouseRightClickToStopMeasure
           3"InBuildWallState"状态的三个动作MouseMoveInMeasure,MouseClickToContinueMeasure,MouseRightClickToStopMeasure
           4 状态下的动作切换到另一个状态
            public void StartHandleMeasureInput()
            {
                fsm = new FSM4InputHandler();
    
    
                var tmpState = new State(fsm, "StartState");
                tmpState.AddAction(new MouseClickToStartMeasure("InBuildWallState"));
                tmpState.AddAction(new MouseMoveBeforeMeasure());
                tmpState.AddAction(new MouseRightClickToStopMeasure());
                fsm.AddState(tmpState);
                fsm.curState = tmpState;
    
                tmpState = new State(fsm, "InBuildWallState");
                tmpState.AddAction(new MouseRightClickToStopMeasure());
                tmpState.AddAction(new MouseClickToContinueMeasure("StartState"));
                tmpState.AddAction(new MouseMoveInMeasure());
                fsm.AddState(tmpState);
    
            }
    
            public void StopHandleInput() // 关闭状态机
            {
                fsm = null;
            }
        }
    }
    

    2.状态控制(入口)

    using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;
    
    namespace SpacePlanner
    {
        public static class FSM_CACHE_KEY
        {
            public static readonly string SELECTED = "SELECTED";
        }
    
        public class FSM4InputHandler
        {
            IDictionary<string, State> states = new Dictionary<string, State>();//所有的状态
    
            public State curState;//当前的状态
            public State GetState(string name)
            {
                return states[name];
            }
            
            public void Run()
            {
                curState = curState.OnStay();
            }
    
            State startState;
            public State StartState
            {
                get
                {
                    return startState;
                }
                set { startState = value; }
            }
    
            public void AddState(State state)
            {
                if (!states.ContainsKey(state.name))
                {
                    states.Add(state.name, state);
                    if (states.Count == 1)
                        StartState = state;
    
                }
            }
        }
    }
    

    3.状态控制(State)

    using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;
    
    namespace SpacePlanner
    {
        /// <summary>
        /// 用户所处的交互状态
        /// </summary>
        public class State
        {
            public string name;//状态的名字
            public FSM4InputHandler fsm;//所属的状态机
    
            public State(FSM4InputHandler fsm, string name)
            {
                this.fsm = fsm;
                this.name = name;
                this.actions = new List<StateAction>();
            }
    
            protected List<StateAction> actions;//在这个状态下的所用动作都保留下来
    
            public void AddAction(StateAction action)
            {
                actions.Add(action);
                action.SetFSM(fsm);
            }
            ____状态机的执行方法与切换________
            /// <summary>
            /// 处于该状态下时需要执行的方法
            /// </summary>
            /// <returns>下一个状态</returns>
            public virtual State OnStay()
            {
                foreach (var action in actions)
                {
                    try
                    {
                        if (action.Execute() && action.nextState != null)
                            return fsm.GetState(action.nextState);
                    }
                    catch (System.Exception e)
                    {
                        Debug.Log(e.ToString());
                        Debug.Log("状态机执行action异常,转到初始状态");
                        return fsm.StartState;
                    }
                }
    
                return this;
            }
        }
    }
    

    4.动作的抽象方法

    using UnityEngine;
    using System.Collections;
    using System;
    
    
    namespace SpacePlanner
    {
        public abstract class StateAction
        {
            public StateAction() { } //没有状态的切换
            public StateAction(string nextState)//有状态的切换
            {
                this.nextState = nextState;
            }
            FSM4InputHandler _fsm;
            public FSM4InputHandler FSM
            {
                get { return _fsm; }
            }
            public void SetFSM(FSM4InputHandler fsm)
            {
                _fsm = fsm;
            }
            public string nextState = null;
            public abstract bool Execute();//这个方法时核心,让每个状态下不同的动作重写该方法,从而达到不一样的效果
        }
        
      
    }
    

    5.动作的执行方法(每个状态下都有多个动作,每个动作的具体效果的实施)

    MouseClickToStartMeasure

    using UnityEngine;
    using System.Collections;
    using System;
    
    namespace SpacePlanner
    {
        public class MouseClickToStartMeasure : StateAction
        {
            public MouseClickToStartMeasure(string nextState) : base(nextState)
            {
            }
    
            public override bool Execute()
            {
                var uiMgr = UIManager.Instance;
                if (uiMgr.ClickOnDrawingPanel())
                {
    #if UNITY_ANDROID || UNITY_IPHONE
                    uiMgr.DrawingPanel.SetAuxiliaryMeasureActive(false);
    #endif
                    Vector3 screenPos = uiMgr.ClickDrawingPanelData.position;
                    Vector3 rwPos = TransformHelper.ScreenToRealWorldPoint(screenPos);
                    Debug.Log("鼠标点击开始测量" + screenPos + rwPos + TransformHelper.ScreenToUISpacePoint(screenPos, uiMgr.DrawingPanel.transform, false));
                    //SPApplication.Instance.SendNotification(MVCNotifications.TO_C_BEGIN_BUILD_WALL, new BoolAndVec(rwPos, true));
                    SPApplication.Instance.SendNotification(MVCNotifications.TO_C_BEGIN_MEASURE, new BoolAndVec(rwPos, true));
                    RulerManager.Instance.ClearCurRulers();
                    return true;
                }
                return false;
            }
        }
    }
    

    MouseMoveBeforeMeasure

    using UnityEngine;
    using System.Collections;
    using System;
    
    namespace SpacePlanner
    {
        public class MouseMoveBeforeMeasure : StateAction
        {
            public MouseMoveBeforeMeasure() : base()
            {
            }
    
            public override bool Execute()
            {
                var uiMgr = UIManager.Instance;
                if (uiMgr.IsPointerOverDrawingPanel())
                {
    #if UNITY_EDITOR || UNITY_STANDALONE
                    Vector3 screenPos = Input.mousePosition;
    #elif UNITY_ANDROID || UNITY_IPHONE
                    Vector3 screenPos = uiMgr.DragDrawingPanelData.position;
    #endif
                    Vector3 rwPos = TransformHelper.ScreenToRealWorldPoint(screenPos);
                    //VOAndVector3 data = new VOAndVector3(null, rwPos);
                    MeasurePointAndVector data = new MeasurePointAndVector(null,rwPos);
                    //Debug.Log("鼠标移动在测量之前"+rwPos);
                    //SPApplication.Instance.SendNotification(MVCNotifications.TO_C_UPDATE_AUXILIARY_LINE, data);
                    SPApplication.Instance.SendNotification(MVCNotifications.TO_C_UPDATE_AUXILIARY_LINE_MEASURE, data);
                    //FPBApplication.Instance.SendNotification(ApplicationConstants.TO_V_POINTER_MOVE_WHEN_DRAW_WALL, screenPos);
                }
    
                return false;
            }
        }
    }
    

    MouseRightClickToStopMeasure

    using UnityEngine;
    using System.Collections;
    using System;
    
    namespace SpacePlanner
    {
        public class MouseRightClickToStopMeasure : StateAction
        {
            public MouseRightClickToStopMeasure() : base()
            {
            }
    
            public override bool Execute()
            {
    //#if UNITY_EDITOR || UNITY_STANDALONE
                if (Input.GetMouseButtonDown(1))
    //#elif UNITY_ANDROID || UNITY_IPHONE
    //            //if (UIManager.Instance.IsTouchReleased())
                     //SendNotification(MVCNotifications.TO_V_CLEAR_AUXILIARY_WALL_MEASURE);// 关掉辅助墙
    //#endif
                {
                    //画墙结束后把UIFloatExitButton隐藏掉
                    //GameObject uiFloatPanel = GameObject.Find("UIFloatExitButton(Clone)");
                    //uiFloatPanel.SetActive(false);   
                    //SPApplication.Instance.SendNotification(MVCNotifications.TO_C_STOP_BUILD_WALL_MEASURE, DrawingSelection.selectedObject);
                    SPApplication.Instance.SendNotification(MVCNotifications.TO_C_STOP_BUILD_WALL_MEASURE, DrawingSelection.SelectMeasurePoint);
                    //DrawingSelection.selectedObject = null;
                    DrawingSelection.SelectMeasurePoint = null;
                    CursorManager.Instance.setNormalCursor();
                    return true;
                }
                return false;
            }
        }
    }
    

    2.测量状态下的动作

    MouseMoveInMeasure

    using UnityEngine;
    using System.Collections;
    using System;
    using SpacePlanner.Core.DataStructure;
    
    namespace SpacePlanner
    {
        public class MouseMoveInMeasure : StateAction
        {
            public MouseMoveInMeasure() : base()
            {
            }
    
            public override bool Execute()
            {
                var uiMgr = UIManager.Instance;
                MeasurePoint measurePoint = DrawingSelection.SelectMeasurePoint;
                if (uiMgr.IsPointerOverDrawingPanel())
                {
    #if UNITY_EDITOR || UNITY_STANDALONE
                    Vector3 screenPos = Input.mousePosition;
    #elif UNITY_ANDROID || UNITY_IPHONE
                    //Vector3 screenPos = uiMgr.DragDrawingPanelData.position;
    
                    Vector3 endPos = uiMgr.DragDrawingPanelData.position;
                    Vector3 dir = (measurePoint.Start - endPos).normalized;
                    float circleRadius = 100f;
                    Vector3 screenPos = endPos + dir * circleRadius;
    #endif
                    Vector3 rwPos = TransformHelper.ScreenToRealWorldPoint(screenPos);
                    Wall wall = DrawingSelection.selectedObject as Wall;
                    VOAndVector3 data = new VOAndVector3(wall, rwPos);
                    //MeasurePoint measurePoint = DrawingSelection.SelectMeasurePoint;
                    MeasurePointAndVector data1 = new MeasurePointAndVector(measurePoint,rwPos);
                    //Debug.Log("在测量时移动鼠标" + screenPos + rwPos);
                    SPApplication.Instance.SendNotification(MVCNotifications.TO_C_UPDATE_AUXILIARY_WALL_MEASURE, data1);
                    SPApplication.Instance.SendNotification(MVCNotifications.TO_C_UPDATE_AUXILIARY_LINE_MEASURE, data1);
                    //FPBApplication.Instance.SendNotification(ApplicationConstants.TO_V_POINTER_MOVE_WHEN_DRAW_WALL, screenPos);
                }
    
                return false;
            }
        }
    }
    

    MouseClickToContinueMeasure

    using UnityEngine;
    using System.Collections;
    using System;
    using SpacePlanner.Core.DataStructure;
    using PureMVC.Patterns;
    
    namespace SpacePlanner
    {
        public class MouseClickToContinueMeasure : StateAction
        {
            public MouseClickToContinueMeasure(string nextState) : base(nextState)
            {
            }
    
            public override bool Execute()
            {
                var uiMgr = UIManager.Instance;
    
                Vector3 screenPos = uiMgr.ClickDrawingPanelData.position;
                Vector3 rwPos = TransformHelper.ScreenToRealWorldPoint(screenPos);
                Debug.Log("鼠标点击结束画一面墙" + screenPos + rwPos + TransformHelper.ScreenToUISpacePoint(screenPos, uiMgr.DrawingPanel.transform, false));
                Wall wall = DrawingSelection.selectedObject as Wall;
                MeasurePoint measurePoint = DrawingSelection.SelectMeasurePoint;
                UndoManager.Instance.AddNotification();
    
    #if UNITY_EDITOR || UNITY_STANDALONE
                if (uiMgr.ClickOnDrawingPanel())
                {
                    
                    //SPApplication.Instance.SendNotification(MVCNotifications.TO_C_BUILD_ONE_WALL_DONE_MEASURE, wall);
                    SPApplication.Instance.SendNotification(MVCNotifications.TO_C_BUILD_ONE_WALL_DONE_MEASURE, measurePoint);
                    //UndoManager.Instance.AddNotification(new Notification(MVCNotifications.TO_C_BUILD_ONE_WALL_DONE, wall));// 添加命令到UndoManager里
                    return true;
                }
    #elif UNITY_ANDROID || UNITY_IPHONE
                if (uiMgr.IsTouchReleased())
                 {
                              
                    //SPApplication.Instance.SendNotification(MVCNotifications.TO_C_BUILD_ONE_WALL_DONE_MEASURE, wall);
                    SPApplication.Instance.SendNotification(MVCNotifications.TO_C_BUILD_ONE_WALL_DONE_MEASURE_ANDROID, measurePoint);               
                    //UndoManager.Instance.AddNotification(new Notification(MVCNotifications.TO_C_BUILD_ONE_WALL_DONE, wall));// 添加命令到UndoManager里
                    return true;
                  }
    #endif
                {
                    RulerManager.Instance.DisplayAllWallRuler();// 刷新标尺
                }
                return false;
            }
        }
    
    
    }
    

    MouseRightClickToStopMeasure

    using UnityEngine;
    using System.Collections;
    using System;
    
    namespace SpacePlanner
    {
        public class MouseRightClickToStopMeasure : StateAction
        {
            public MouseRightClickToStopMeasure() : base()
            {
            }
    
            public override bool Execute()
            {
    //#if UNITY_EDITOR || UNITY_STANDALONE
                if (Input.GetMouseButtonDown(1))
    //#elif UNITY_ANDROID || UNITY_IPHONE
    //            //if (UIManager.Instance.IsTouchReleased())
                     //SendNotification(MVCNotifications.TO_V_CLEAR_AUXILIARY_WALL_MEASURE);// 关掉辅助墙
    //#endif
                {
                    //画墙结束后把UIFloatExitButton隐藏掉
                    //GameObject uiFloatPanel = GameObject.Find("UIFloatExitButton(Clone)");
                    //uiFloatPanel.SetActive(false);   
                    //SPApplication.Instance.SendNotification(MVCNotifications.TO_C_STOP_BUILD_WALL_MEASURE, DrawingSelection.selectedObject);
                    SPApplication.Instance.SendNotification(MVCNotifications.TO_C_STOP_BUILD_WALL_MEASURE, DrawingSelection.SelectMeasurePoint);
                    //DrawingSelection.selectedObject = null;
                    DrawingSelection.SelectMeasurePoint = null;
                    CursorManager.Instance.setNormalCursor();
                    return true;
                }
                return false;
            }
        }
    }
    

    总结一下状态机的流程(注意事项)
    1.有状态切换的动作,stateAction的构造函数不一样,需要把状态传过去。
    2.状态,动作,动作改变状态。

    相关文章

      网友评论

          本文标题:状态机_fsm(Finite State Machine)

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