有限状态机这种东西 或者说状态这种东西 真的是你运用到了,才能更好的理解。啥是状态,其实这个在开发者思维里,就是一种阶段,或者某个时刻,某个条件下的
事物的集合,例如一个角色的走路状态,休息状态,跑步状态,或者一个UI菜单面板的保存,加载,退出,新建,你都可以把他们归为一个一个状态。甚至游戏的,登录,登录验证,登录成功,登录失败,初始化都可以为一个一个状态。看来状态机真的是用到的地方非常多的,并不一定是在角色身上的状态,很多时候,只要是不同的逻辑划分,我们就可以归为一个状态。
我举例的状态机是别人 商业级的代码剥离出来的,代码是转载的,文笔不好,还是直接上代码解释。
首先是状态机,状态机管理着,状态的进入,状态的退出,状态的执行,状态机里有一个严格的规定
那就是,当有一个新状态进入的时候,必定会有当前的状态的退出,这个很重要,状态的执行由状态机掌控,这个状态机设计应该是最简单的了,我们如果能够熟练运用后,在碰到实际问题时候,我们才能够在这个的基础上来修改,完善它。可以加上自己的见解,然后抽象出来,形成一个新的功能更加强大的状态机,要理解这个状态机,还得了解泛型编程,理解泛型编程才能够很好的理解这个状态机。
基础状态机如下
using UnityEngine;
using System.Collections;
/// <summary>
/// 状态机
/// 使用这个来驱动各种状态
/// </summary>
/// <typeparam name="entity_type"></typeparam>
public class FsmStateMachine<entity_type>
{
//状态持有对象
private entity_type m_pOwner;
//
private FsmState<entity_type> m_pCurrentState;//当前状态
private FsmState<entity_type> m_pPreviouState;//上一个状态
private FsmState<entity_type> m_pGlobalState;//全局状态
/// <summary>
/// 状态机的构造函数
/// </summary>
/// <param name="owner"></param>
public FsmStateMachine(entity_type owner)
{
m_pOwner = owner;
m_pPreviouState = null;
m_pCurrentState = null;
m_pGlobalState = null;
}
/// <summary>
/// 设置当前状态
/// </summary>
/// <param name="GlobalState"></param>
public void SetCurrentState(FsmState<entity_type> CurrentState)
{
if (CurrentState != null)
{
//保存 当前状态
m_pCurrentState = CurrentState;
//设置 状态中的 Target 为
m_pCurrentState.Target = m_pOwner;
m_pCurrentState.Enter();
}
}
/// <summary>
/// 进入全局状态
/// 进行一些数据的刷新,比如说距离的计算等
/// </summary>
/// <param name="GlobalState"></param>
public void SetGlobalState(FsmState<entity_type> GlobalState)
{
if (GlobalState != null)
{
m_pGlobalState = GlobalState;
m_pGlobalState.Target = m_pOwner;
m_pGlobalState.Enter();
}
else
{
Debug.LogError("不能设置空状态");
}
}
/// <summary>
/// 进入全局状态d
/// </summary>
public void GlobalStateEnter()
{
}
/// <summary>
/// Update方法
/// </summary>
public void SmUpdate()
{
//只要设置了.就会一直执行
if (m_pGlobalState != null)
{
m_pGlobalState.Excute();
}
if (m_pCurrentState != null)
{
m_pCurrentState.Excute();
}
}
/// <summary>
/// 切换状态
/// </summary>
/// <param name="pNewState">希望变成的状态</param>
public void ChangeState(FsmState<entity_type> pNewState)
{
if (pNewState == null)
{
Debug.Log("不能使用空的状态");
}
//触发状态退出方法
m_pCurrentState.Exit();
//保存上一状态
m_pPreviouState = m_pCurrentState;
//设置新状态为当前状态
m_pCurrentState = pNewState;
//传递 状态 使用 对象
m_pCurrentState.Target = m_pOwner;
//触发当前状态调用Enter方法
m_pCurrentState.Enter();
}
/// <summary>
/// 切换回上一状态
/// </summary>
public void RevertToPreviousState()
{
this.ChangeState(m_pPreviouState);
}
/// <summary>
/// 获取当前状态
/// </summary>
/// <returns></returns>
public FsmState<entity_type> Get_CurrentState()
{
return m_pCurrentState;
}
public FsmState<entity_type> Get_GlobalState()
{
return m_pGlobalState;
}
/// <summary>
/// 获取上一状态
/// </summary>
/// <returns></returns>
public FsmState<entity_type> Get_PreviousState()
{
return m_pPreviouState;
}
}
接下来我们实现状态基类
状态类很简单,就是进入,退出,执行,还有一个使用状态的对象
using UnityEngine;
/// <summary>
/// 状态基类,定义了状态的基础方法
/// 为什么需要定义为3个呢
/// 是因为有些逻辑都是进入状态或者退出状态的时候执行的,
/// 所以这里将逻辑分开:
/// 分为:
/// 1>进入状态,
/// 2>状态执行,
/// 3>状态退出
/// </summary>
/// <typeparam name="entity_type">使用泛型,哪个状态使用,就传入哪个状态</typeparam>
public class FsmState<entity_type>
{
/// <summary>
/// 使用状态的对象
/// </summary>
public entity_type Target;
/// <summary>
/// 进入状态的逻辑
/// 只执行一次的逻辑
/// </summary>
/// <param name="entityType"></param>
public virtual void Enter()
{
Debug.Log(this.ToString()+" Enter");
}
/// <summary>
/// 执行状态的逻辑
/// </summary>
/// <param name="entityType"></param>
public virtual void Excute()
{
}
/// <summary>
/// 退出状态的逻辑
/// </summary>
/// <param name="entityType"></param>
public virtual void Exit()
{
Debug.Log(this.ToString() + " Exit");
}
}
好了,状态机基本搭建好了,是不是很简单,其实简单就好,功能复杂的功能都是分解成一个一个小功能去实现的,何况是基层代码,越简单越好,越简单越稳,然后呢,我们要的就是这种基层代码的约束,比如,有状态退出,必定有状态的进入,我觉得这个就是状态机的核心功能,在这个约束规则上做我们的事。上层代码才能够很好的挥洒功能。
下面我们就新建一个简单的状态机,实际运行,才能够加深理解。
首先是状态机
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public enum CubeState
{
Run,
Idle,
}
public class CubeAI : MonoBehaviour
{
FsmStateMachine<CubeAI> m_Machine;//状态机对象用来管理状态的
public Dictionary<CubeState, FsmState<CubeAI>> StateDic; //状态字典,用来存放状态
void Awake()
{
StateDic = new Dictionary<CubeState, FsmState<CubeAI>>();
StateDic.Add(CubeState.Run, new CubeRun());
StateDic.Add(CubeState.Idle, new CubeIdle());
}
// Use this for initialization
void Start()
{
m_Machine = new FsmStateMachine<CubeAI>(this);
m_Machine.SetCurrentState(StateDic[CubeState.Idle]);
}
// Update is called once per frame
void Update()
{
m_Machine.SmUpdate();
}
public void ChangeAIState(CubeState state)
{
m_Machine.ChangeState(StateDic[state]);
}
}
接着是休息 跑步的状态
using UnityEngine;
using System.Collections;
public class CubeIdle : FsmState<CubeAI>
{
int idleTime = 0;
float delateTime = 0;
int i = 0;
public override void Enter()
{
base.Enter();
Debug.Log("Cube 进入了人生思考阶段");
i = 0;
delateTime = 0;
idleTime = Random.Range(8, 16);
}
public override void Excute()
{
base.Excute();
i++;
Debug.Log("Cube 在思考人生" + i);
delateTime += Time.deltaTime;
if (delateTime >= idleTime)
{
Target.ChangeAIState(CubeState.Run);
}
}
public override void Exit()
{
base.Exit();
Debug.Log("Cube 结束了人生思考");
i = 0;
}
}
using UnityEngine;
using System.Collections;
public class CubeRun : FsmState<CubeAI>
{
int runTime = 0;
float delateTime = 0;
public override void Enter()
{
base.Enter();
Debug.Log("Cube 进入了人生思考阶段");
runTime = Random.Range(5, 10);
delateTime = 0;
}
public override void Excute()
{
base.Excute();
Target.transform.Translate(Vector3.forward * 1 * Time.deltaTime);
Debug.Log("Cube 快乐的奔跑中,跑到了" + Target.transform.position);
delateTime += Time.deltaTime;
if (delateTime >= runTime)
{
Target.ChangeAIState(CubeState.Idle);
}
}
public override void Exit()
{
base.Exit();
Debug.Log("Cube 跑类了");
}
}
理解完可能需要一点时间,其实可以用到项目上了,无非就是状态的切换,然后在状态进入的时候,操作什么,状态进入后执行什么,状态退出后操作什么。状态机的作用就是让你清楚你自己的逻辑,不至于代码混乱,思维混乱,写代码最忌混乱,一乱BUG便会找上门。当你熟悉这个机制后,可以尝试的把自己的理解加进去,来充实,强壮这个状态机。当你熟悉之后,你会发现,它的用处无处不在。