- 最后登录
- 2016-8-29
- 注册时间
- 2012-8-25
- 阅读权限
- 90
- 积分
- 23585
- 纳金币
- 20645
- 精华
- 62
|
关于状态机,我想都知道它很常用,就像单例一样,我们基本上每个项目都会用到它。网上也有很多大神写的状态机,比如 PlayMaker 这样重量级的插件,功能很强大,依赖也很多。不过,程序猿的胃口有时候就是很挑剔,吃惯了大鱼大肉的东西,总想吃点清粥小菜。应该有很多朋友跟我一样,希望有一套轻量级的状态机实现,每行代码自己都了如指掌,实现我们一些简单的状态控制的需求。In hand(伸手就来的方便),你也有这种共鸣么?
下面是我的实现,有不足或者 low的地方,希望告诉我,一起进步吧!
状态机类:- using UnityEngine;
- using System.Collections;
- using System.Collections.Generic;
- namespace PLY.StateMachine {
- /// <summary>
- /// 有限状态机基类.
- /// </summary>
- [System.Serializable]
- public class FiniteStateMachine : MonoBehaviour {
- private object Context_;
- public string CurrentStateName = "EmptyState";
- private FiniteState CurrentState_;
- protected FiniteState CurrentState{
- get{
- return CurrentState_;
- }
- set{
- CurrentState_ = value;
- CurrentStateName = CurrentState_.Name;
- }
- }
- protected FiniteState defaultState;
- private Dictionary<string, FiniteState> allStates_ = new Dictionary<string, FiniteState>();
- private List<FiniteStateTransition> allTransitions_ = new List<FiniteStateTransition>();
- protected virtual void Init(){}
- protected void AddState(FiniteState state){
- allStates_.Add(state.Name, state);
- }
- protected void RemoveState(FiniteState state){
- allStates_.Remove(state.Name);
- }
- /// <summary>
- /// 添加过渡.
- /// </summary>
- /// <param name="eventName">Event name.</param>
- /// <typeparam name="T1">源状态类型.</typeparam>
- /// <typeparam name="T2">目标状态类型.</typeparam>
- protected void AddTransition<T1,T2>(string eventName){
- string lastStateName = typeof(T1).Name;
- string nextStateName = typeof(T2).Name;
- FiniteStateTransition transition = new FiniteStateTransition(eventName, lastStateName, nextStateName );
- allTransitions_.Add(transition);
- }
- protected void AddTransition(FiniteStateTransition transition){
- allTransitions_.Add(transition);
- }
- protected void RemoveTransation(FiniteStateTransition transition){
- allTransitions_.Remove(transition);
- }
-
- public void SetContext(object context){
- Context_ = context;
- }
- /// <summary>
- /// 发送事件,触发下一状态的切换
- /// </summary>
- /// <param name="eventName">事件名.</param>
- public void SendEvent(string eventName){
- for (int i = 0; i < allTransitions_.Count; i++) {
- FiniteState nextState;
- if (TryGetNextStateByEventName(allTransitions_[i], eventName, out nextState)){
- ChangeState(nextState);
- break;
- }
- }
- }
- private bool TryGetNextStateByEventName(FiniteStateTransition transation, string eventName, out FiniteState state){
- state = null;
- if (CurrentState == null || !CurrentState.Name.Equals(transation.LastStateName)) return false;
- if (!eventName.Equals(transation.EventName)) return false;
- return (allStates_.TryGetValue(transation.NextStateName, out state));
- }
- /// <summary>
- /// 切换状态.
- /// </summary>
- /// <param name="newState">New state.</param>
- private void ChangeState(FiniteState newState){
- if (CurrentState != null){
- CurrentState.OnExit(Context_);
- }
- else{
- Debug.LogError("当前状态为空,退出状态失败!", gameObject);
- }
- CurrentState = newState;
- if(CurrentState != null){
- CurrentState.OnEnter(Context_);
- }
- else{
- Debug.LogError("当前状态为空,进入状态失败!", gameObject);
- }
- }
- private void SetDefaultToCurrent(){
- CurrentState = defaultState;
- }
- private void Awake(){
- Init();
- SetDefaultToCurrent();
- }
- private void Start () {
- if (CurrentState != null){
- CurrentState.OnEnter(Context_);
- }
- }
- private void Update () {
- if (CurrentState != null){
- CurrentState.OnUpdate(Context_);
- }
- }
- private void FixedUpdate(){
- if (CurrentState != null){
- CurrentState.OnFixedUpdate(Context_);
- }
- }
- private void LateUpdate(){
- if (CurrentState != null){
- CurrentState.OnLateUpdate(Context_);
- }
- }
- }
- }
复制代码 Transition 和状态基类:- using UnityEngine;
- using System.Collections;
- namespace PLY.StateMachine{
- /// <summary>
- /// 状态机过渡约束基类.
- /// </summary>
- public class FiniteStateTransition {
- public string EventName;
- public string LastStateName;
- public string NextStateName;
- public FiniteStateTransition(string eventName, string lastStateName, string nextStateName){
- EventName = eventName;
- LastStateName = lastStateName;
- NextStateName = nextStateName;
- }
- }
- /// <summary>
- /// 状态基类.
- /// </summary>
- public class FiniteState {
- public string Name {
- get {
- return GetType().Name;
- }
- }
- public virtual void OnEnter(object context){}
- public virtual void OnExit(object context){}
- public virtual void OnUpdate(object context){}
- public virtual void OnFixedUpdate(object context){}
- public virtual void OnLateUpdate(object context){}
- }
- }
复制代码 具体的测试状态机及状态的定义:- using UnityEngine;
- using System.Collections;
- using PLY.StateMachine;
- public class PlayerStateMachine : FiniteStateMachine{
- protected override void Init (){
- Locked state1 = new Locked();
- AddState(state1);
-
- Unlocked state2 = new Unlocked();
- AddState(state2);
- defaultState = state1;
- AddTransition<Locked, Unlocked>("UNLOCK");
- AddTransition<Unlocked, Locked>("LOCK");
- }
- }
- public class Locked : FiniteState{
- public override void OnEnter (object target){
- Player owner = target as Player;
- owner.Show(true);
- }
-
- public override void OnExit (object target){
- }
- public override void OnUpdate (object target){
- }
- }
- public class Unlocked : FiniteState{
-
- public override void OnEnter (object target){
- Player owner = target as Player;
- owner.Show(false);
- }
-
- public override void OnExit (object target){
- }
-
- public override void OnUpdate (object target){
- }
-
- }
复制代码 宿主脚本:- using UnityEngine;
- using System.Collections;
- using PLY.StateMachine;
- public class Player : MonoBehaviour{
- public FiniteStateMachine StateMachine;
- private bool isShowingLockedView_;
-
- private Texture LockTex_;
- private Texture UnlockTex_;
- private void Awake(){
- StateMachine.SetContext(this);
- LockTex_ = Resources.Load("tex_locked") as Texture;
- UnlockTex_ = Resources.Load("tex_unlocked") as Texture;
- }
- private void OnGUI(){
- if(GUI.Button(new Rect(0,0,120,30), "UNLOCK")){
- StateMachine.SendEvent("UNLOCK");
- }
- if(GUI.Button(new Rect(0,40,120,30), "LOCK")){
- StateMachine.SendEvent("LOCK");
- }
- if(isShowingLockedView_){
- GUI.Label(new Rect(Screen.width/2,50,128,128), new GUIContent(LockTex_));
- }
- else{
- GUI.Label(new Rect(Screen.width/2,50,128,128), new GUIContent(UnlockTex_));
- }
- }
- public void Show(bool isLocked){
- isShowingLockedView_ = isLocked;
- }
- }
复制代码 |
|