一.状态机
状态机
二.个人理解的状态机
个人理解的状态机.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.状态,动作,动作改变状态。
网友评论