有限状态机的定义
表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型 顾名思义 就是所有的状态都会在状态机的集合里,方便管理。在启动一个FSM时,首先必须将FSM置于“起始状态”,然后输入一系列操作,最终,FSM会到达“结束状态”
实现一个简单的状态机
///定义一个抽象的类,具体的函数在子类中实现,是状态机的基类
public abstruct class FSMState
{
protected string name;//状态的名字
public string Name{get=>name;}//name接口 对外界调用
protected FSMSystem fsm;
protracted Dictionary<Transaction , string>statemap=new Dictionary<Transaction , string>();//存放所有状态的字典 Transaction是转换条件 等一会实现 string 是名字
///添加转换条件 首先判断转换条件是不是空 如果为空不执行 在判断字典中是否有这个转换条件 如果没有再加入
public void AddTransaction(Transaction transaction,string name)
{
if(transaction==Transaction.NULL||name=="") return;
if(!map.ContainKey(transaction))map.add(transaction,name);
}
//删除转换条件 思路和插入一样
public void DestoryTransaction(Transaction transaction)
{
if (transaction == Transaction.NULL) return;
if (map.ContainsKey(transaction)) map.Remove(transaction);
}
///获取当前状态 如果字典中有该状态就返回他的名字 否则返回空
public string GetOutState(Transaction transaction)
{
if(map.ContainsKey(transaction) return statemap[transaction];
else return"";
}
///状态进入之前做
public virtual void DoBeforEntering(){}
///状态离开之前做
public virtual void DoBeforLeaving(){}
///检测状态转换
public abstract void Reason();
///控制该状态的行为
public abstract void Act();
}
补充 abstract和virtual的区别(面试经常问)
共同点: 两个都是为了让子类对付类重新定义 覆盖父类 区别:1 在父类中virtual修饰的方法必须有实现 而abstract修饰的方法不能有实现 必须用“;”结束 2 virtual在子类中可以被重写 abstruct必须被重写 3 如果类中有一个函数被abstract修饰那么类名也必须用abstract修饰 4 abstract类不能被实例化 必须依靠子类
言归正传 现在写管理状态的类
///使用一个List列表把状态全部管理
public class FSMSystem
{
private List<FSMState> stateList ;
private FSMState nowState;///当前的状态
public FSMState NowState { get => nowState; }
public FSMSystem()
{
stateList=new List<FSMState>();
}
///添加状态
///如果没有就跳出 如果有则判断list是不是为空 如果为空就把第一个进入的状态设为默认的状态
///如果list不为空那么就判断list里面有没有这个状态 如果没有则加入
public void AddState(FSMState state)
{
if(state==null)return;
else if(stateList.Count==0)
{
stateList.Add(state);
nowstate=state;
return;
}
else if(!stateList.Contains(state))
stateList.Add(state);
}
///删除状态
public void DestoryState(string stateName)
{
if (stateName == "") return;
foreach(FSMState state in stateList)
{
if(state.Name==stateName)
{
stateList.Remove(state);
break;
}
}
}
///转换状态
public void DoTransaction (Transaction transaction)
{
if(transaction==Transaction.NULL)
return;
string newstateName=nowState.GetOutState(transaction);
if(newstateName=="")return;
else
{
foreach(FSMState state in stateList)
{
if(state.Name==newstateName)
{
nowState.DoBeforLeaving();
nowstate=state;
nowState.DoBeforEntering();
retrun;
}
}
}
}
}
实现Transaction (状态)
public enum Transaction
{
NULL,
MOVE,
IDEL
}
这样框架就完整实现了 现在我来写个小Demo来展示这个框架的用法
MoveState(移动状态)
public class MoveState:FSMState()
{
Transform movetrans;
public MoveState(Transform target,FSMSystem f)
{
this.name="Move";
this.movetrans=target;
this.fsm=f;
}
///重写这个状态的要做的事情
public override void Act()
{
Debug.Log("移动了!");
}
///切换状态
public override void Reason()
{
if (Input.GetKeyDown(KeyCode.W))
fsm.DoTransaction(Transaction.TO_IDEL);
}
}
静止状态
public class idelState :FSMState
{
public IdelState(FSMSystem f)
{
this.name="Idel";
this.fsm=f;
}
public override void Act()
{
Debug.Log("静止");
}
public override void Reason()
{
if(Input.GetKeyDown(KeyCode.Space))
fsm.DoTransaction(Transaction.TO_MOVE);
}
}
玩家或敌人代码
public class SleepControl:MonoBehaviour
{
private FSMSystem fsm;
public Transform target;
///进行初始化操作
private void MakeFSM()
{
fsm=new FSMSystem();
IdelState idel=new IdelState(fsm);
idel.AddTransaction(Transaction.MOVE,"Move");
MoveState move=new MoveState(target,fsm);
move.AddTransaction(Transaction.IDEL,"Idel");
fsm.AddState(idel);
fsm.AddState(move);
}
private void Awake()
{
MakeFSM();
}
private void Update()
{
fsm.nowState.Act();
fsm.nowState.Reason();
}
}
这样就可以按空格和W切换不同的状态了,如果要用自己想要在某个状态要做的事就可以直接在Act函数中调用你想要的操作的函数接口即可
|