状态机_fsm(Finite State Machine)

一.状态机


状态机

二.个人理解的状态机


个人理解的状态机.jpg

三.自己搭建一个状态机


搭建状态机步骤

状态机,管理多个状态,每个状态有多个动作,状态下动作的改变切换到另一个状态
1.创建状态机的管理(Manager)
using UnityEngine;
using System.Collections;

namespace SpacePlanner
{
    /// 
    /// 处理用户的一些复杂输入,并不是全部
    /// 尤其是那些一步一步进行的操作
    /// 
    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();
                }
                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;
        }


        /// 
        /// Alan添加的建立户型状态机控制
        /// 
        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 states = new Dictionary();//所有的状态

        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
{
    /// 
    /// 用户所处的交互状态
    /// 
    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();
        }

        protected List actions;//在这个状态下的所用动作都保留下来

        public void AddAction(StateAction action)
        {
            actions.Add(action);
            action.SetFSM(fsm);
        }
        ____状态机的执行方法与切换________
        /// 
        /// 处于该状态下时需要执行的方法
        /// 
        /// 下一个状态
        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))