基于以上两点,这种方式才符合开闭原则【不知道 开闭原则 的请自行查询】;
首先我们先构建状态类的基类:FSMState
using System;
using System.Collections.Generic;
using UnityEngine;
///
/// 变换条件 枚举
///
public enum Transition
{///
/// 空的转换条件
///
Null=0,
///
/// 对应第一个转换条件
///
One,
Two
}
public enum StateID
{///
/// 空状态
///
Null = 0,
///
/// 对应第一个装换状态
///
One,
Two
}
///
/// 状态机的状态类 没一个具体的状态需要继承其 并重写一些函数
///
public abstract class FSMState
{
///
///状态的ID 会被每一个子类继承过去
///
protected StateID stateID;
///
/// 获取状态ID 属性
///
public StateID StateID { get { return stateID; } }
///
/// 每个条件对应的状态存储字典 会被每一个子类继承过去
///
protected Dictionary map = new Dictionary();
///
/// 状态所属的状态机
///
protected FSMSystem fSM;
public FSMState(FSMSystem fsm)
{
fSM = fsm;
}
///
/// 往状态机里添加转换条件和条件对应的状态
///
/// 条件
/// 状态ID
public void AddTransition(Transition tr, StateID ID)
{
if (tr == Transition.Null)
{
Debug.LogError("转换条件不能为空:【AddTransition】");
return;
}
if (ID == StateID.Null)
{
Debug.LogError("状态不能为空:【AddTransition】");
return;
}
if (map.ContainsKey(tr))
{
Debug.LogError(tr+"已经存在:【AddTransition】");
return;
}
map.Add(tr, ID);
}
///
/// 删除某一状态转换
///
///
public void DeleteTransition(Transition tr)
{
if (!map.ContainsKey(tr))
{
Debug.LogError("状态不存在 无法删除");
return;
}
map.Remove(tr);
}
///
/// 通过转换条件 获取对应的状态 多用在获取要转换的下一个状态时
///
///
///
public StateID GetStateIDByTransition(Transition tr)
{
if (!map.ContainsKey(tr))
{
Debug.Log("该状态不存在于状态机中,");
}
return map[tr];
}
///
/// 进入状态的第一针调用 多用于 初始化该状态的一些信息
///
public virtual void EnterInto()
{
}
///
/// 状态离开时调用 多用于 用来对该状态善后
///
public virtual void Leaving()
{
}
///
/// 该状态执行的行为 没帧调用
///
public abstract void Action(GameObject[] obj);
///
/// 判断当前状态是否需要转换到其他状态 ,没帧调用
///
public abstract void Reason(GameObject[] obj);
}
然后创建状态机类:
using System;
using System.Collections.Generic;
using UnityEngine;
public class FSMSystem
{
///
/// 存储当前状态机的每个ID对应的状态
///
private Dictionary states=new Dictionary();
///
/// 当前状态ID
///
private StateID currentStateID;
///
/// 当前状态
///
private FSMState currentState;
///
/// 添加状态机的状态
///
///
public void addState(FSMState state)
{
if (state == null)
{
Debug.LogError("要添加的状态为空");
return;
}
if (states.ContainsKey(state.StateID))
{
Debug.LogError("当前状态已经存在:" + state.ToString() + ";不能重复添加");
return;
}
//如果当前状态机没有正在进行的状态 则吧这个加入状态机的状态设置成当前状态 当然这里也可以吧这个放在状态机的构造函数里直接构造出来
if (currentState == null)
{
currentStateID = state.StateID;
currentState = state;
}
states.Add(state.StateID, state);
}
///
/// 删除某一ID 一般用不到
///
///
public void DeleteState(StateID id)
{
if (id == StateID.Null)
{
Debug.LogError("要删除的状态不能是空状态");
return;
}
if (!states.ContainsKey(id))
{
Debug.LogError("要删除的状态不存在:" + id);
return;
}
states.Remove(id);
}
///
/// 执行状态转变 在状态里 判断状态是否需要转换的时候 需要转换的时候转换;
///
///
public void PerformTransition(Transition tr)
{
if (tr == Transition.Null)
{
Debug.LogError("不能转换成空条件");
return;
}
StateID id = currentState.GetStateIDByTransition(tr);
if (id == StateID.Null)
{
Debug.LogError("当前要转换到的状态为空状态");
}
if (!states.ContainsKey(id))
{
Debug.LogError("当前状态不存在于状态机中 无法转换"+ id);
}
//校验完毕 执行转换
//未转换状态时 执行状态离开操作
currentState.Leaving();
//更换状态
currentState = states[id];
currentStateID = id;
//执行状态进入的操作
currentState.EnterInto();
}
///
/// 执行当前状态的行为方式;
///
/// 这里的OBJ是用来判断转换条件和行为对象的 可以直接在具体状态中用字段存储 在状态进入时用进入状态的函数初始化
public void Update( GameObject[] obj)
{
currentState.Reason(obj);
currentState.Action(obj);
}
}
上面就是状态机的基础 下面我们来看看怎么使用:
using System;
using System.Collections.Generic;
using UnityEngine;
public class OneState : FSMState
{
///
/// 实现构造方法
///
///
public OneState(FSMSystem fsm) : base(fsm)
{
}
public override void Action(GameObject[] obj)
{
//这里写上这个状态所需要执行的东西
}
public override void Reason(GameObject[] obj)
{
//这里执行这个状态转换到其他状态的判断
}
}
第二个具体状态类:
using System;
using System.Collections.Generic;
using UnityEngine;
public class TwoState : FSMState
{
///
/// 实现构造方法
///
///
public TwoState(FSMSystem fsm) : base(fsm)
{
}
public override void Action(GameObject[] obj)
{
//这里写上这个状态所需要执行的东西
}
public override void Reason(GameObject[] obj)
{
//这里执行这个状态转换到其他状态的判断
}
}
有限状态机的使用者:
using System;
using System.Collections.Generic;
using UnityEngine;
///
/// 这个是使用有限状态机的 比如玩家 怪物 等等
///
public class UseFSM:MonoBehaviour
{
private FSMSystem fSMSystem;
private void Awake()
{
MakeFSM();
}
private void Update()
{
fSMSystem.Update(new GameObject[] { });//这里传入当前状态判断转换条件是所需要依赖的对象 比如 怪物判断是否能攻击时 需要传入玩家 因为需要借助玩家计算攻击距离是否足够
//这里的另一种方式是将判断所依赖的对象在状态里进行获取 并在该状态的 EnterInto 方法中初始化;
}
void MakeFSM()
{
fSMSystem = new FSMSystem();
//制作状态并设置该状态的转换条件
FSMState one = new OneState(fSMSystem);
one.AddTransition(Transition.One, StateID.One);
FSMState two = new TwoState(fSMSystem);
two.AddTransition(Transition.Two, StateID.Two);
//将状态加入到状态机中
fSMSystem.addState(one);
fSMSystem.addState(two);
}
}
源码链接:https://pan.baidu.com/s/1mi8A46O 密码:wf6d