该状态机逻辑
测试代码
using UnityEngine;
public class C_FSM_Test: MonoBehaviour
{
C_FSMCtrl fsmC;
C_FSMState stateA; //也可以通过 fsmC.dicALLStates["StateA"] 来获取,此处演示,直接拿到stateA实例
// Use this for initialization
void Start()
{
fsmC = new C_FSMCtrl(this.name);
stateA = fsmC.AddState("StateA", () => { Debug.Log("A Start!!!"); }, () => { Debug.Log("A Over!!!"); }, () => { Debug.Log("A Stay Continue Print"); });
fsmC.AddState("StateB", () => { Debug.Log("B Start!!!"); }, () => { Debug.Log("B Over!!!"); });
fsmC.AddState("StateC", () => { Debug.Log("C Start!!!"); }, () => { Debug.Log("C Over!!!"); });
//通过指定父状态C_FSMState对象的方式添加当前状态到stateA状态
fsmC.AddState("StateD", () => { Debug.Log("E Start!!!"); }, () => { Debug.Log("E Over!!!"); },null, stateA);
fsmC.AddState("StateE", () => { Debug.Log("F Start!!!"); }, () => { Debug.Log("F Over!!!"); },null, stateA);
fsmC.AddState("StateF", () => { Debug.Log("G Start!!!"); }, () => { Debug.Log("G Over!!!"); },null, stateA);
//通过指定父状态字符串的方式添加状态
fsmC.AddState("StateG", "StateB", () => { Debug.Log("G Start!!!"); }, () => { Debug.Log("G Over!!!"); });
fsmC.AddState("StateH", "StateC", () => { Debug.Log("H Start!!!"); }, () => { Debug.Log("H Over!!!"); });
fsmC.AddState("StateI", "StateC", () => { Debug.Log("I Start!!!"); }, () => { Debug.Log("I Over!!!"); });
fsmC.AddState("StateJ", "StateD", () => { Debug.Log("J Start!!!"); }, () => { Debug.Log("J Over!!!"); });
fsmC.AddState("StateK", "StateD", () => { Debug.Log("K Start!!!"); }, () => { Debug.Log("K Over!!!"); });
fsmC.AddState("StateO", "StateH", () => { Debug.Log("O Start!!!"); }, () => { Debug.Log("O Over!!!"); });
fsmC.AddState("StateL", "StateJ", () => { Debug.Log("L Start!!!"); }, () => { Debug.Log("L Over!!!"); });
fsmC.AddState("StateM", "StateK", () => { Debug.Log("M Start!!!"); }, () => { Debug.Log("M Over!!!"); });
fsmC.AddState("StateN", "StateK", () => { Debug.Log("N Start!!!"); }, () => { Debug.Log("N Over!!!"); });
}
// Update is called once per frame
void Update()
{
//为什么在此处调用,如果在FSMState自身调用,那么所有的状态都会调用,即使它没有任何实质函数内容
//但放在此处可以按需调用,也可以放在FixedUpdate中调用,或者定时调用,更加灵活
stateA.stay();
if (Input.GetKeyDown(KeyCode.O))
{
Debug.Log("按下启动O状态");
fsmC.StartState("StateO");
}
if (Input.GetKeyDown(KeyCode.M))
{
Debug.Log("按下启动M状态");
fsmC.StartState("StateM");
}
if (Input.GetKeyDown(KeyCode.N))
{
Debug.Log("按下启动N状态");
fsmC.StartState("StateN");
}
}
}
using System;
using System.Collections.Generic;
/// <summary>
/// FSM状态
/// </summary>
public class C_FSMState
{
//当前状态是否处于激活状态
public bool isActivity;
//当前状态的路径,路径记录了父类状态名称 例如 ROOT/StateA/StateB/StateNow
public string path;
//记录当前状态开始时的委托
public Action act_start;
//记录当前状态结束时的委托
public Action act_over;
//记录当状态stay驻留时候的委托,这个方法可以放在绑定物体的update()函数中重复执行
public Action act_stay;
//记录当前状态所属于的状态控制器
public C_FSMCtrl fsmCtrl; //控制器
//当前状态名称
public string StateName;
//当前状态的所属的父状态
public C_FSMState StateFather;
//当前状态的子状态容器
public Dictionary<string, C_FSMState> dicChilds; //子节点
/// <summary>
/// 仅包含一个名称的状态的构造函数
/// </summary>
/// <param name="stateName">状态名</param>
public C_FSMState(string stateName)
{
StateName = stateName;
path = StateName;
}
/// <summary>
/// 一个状态的构造函数
/// </summary>
/// <param name="stateName">状态名</param>
/// <param name="_act_start">当前状态开始时的委托</param>
/// <param name="_act_over">当前状态关闭时的委托</param>
public C_FSMState(string stateName, Action _act_start = null, Action _act_over = null, Action _act_stay = null)
{
StateName = stateName;
act_start = _act_start;
act_over = _act_over;
act_stay = _act_stay;
path = StateName;
}
/// <summary>
/// 开始一个状态
/// </summary>
public void start()
{
//若当前状态已经激活
if (isActivity == true)
{
return;
}
//如果当前状态的父状态不为空,切父状态未开启,则先开启父状态
if (this.StateFather != null && this.StateFather.isActivity == false)
{
this.StateFather.start();
}
//将当前状态至于开启状态
isActivity = true;
//设置当前状态控制器为当前状态为此状态
fsmCtrl.currentState = this;
//如果设置了开启状态的回调函数,则进行回调
if (act_start != null)
{
act_start();
}
}
/// <summary>
/// 记录当状态stay驻留时候的委托,这个方法可以放在绑定物体的update()函数中重复执行
/// </summary>
public void stay()
{
if (isActivity && act_stay!=null)
{
act_stay();
}
}
/// <summary>
/// 状态结束
/// </summary>
public void over()
{
//如果当前状态已经处于结束状态则返回
if (isActivity == false)
{
return;
}
//如果当前状态存在子状态
if (this.dicChilds != null)
{
//则逐级关闭当前状态
var emut = dicChilds.GetEnumerator();
while (emut.MoveNext())
{
emut.Current.Value.over();
}
}
//将当前状态激活状态关闭
isActivity = false;
//如果状态控制器的 当前状态为自己,则关闭自己后将其设置为父状态
if (fsmCtrl.currentState != null && fsmCtrl.currentState == this)
{
fsmCtrl.currentState = StateFather;
}
//如果当前状态的关闭委托不为空,则调用当前委托
if (act_over != null)
{
act_over();
}
}
}
using System;
using System.Collections.Generic;
public class C_FSMCtrl
{
private string rootStateName = "root"; //根状态名
public C_FSMState rootState; //根状态
public C_FSMState currentState; //当前状态
public string ctrlName; //状态控制器名称
public Dictionary<string, C_FSMState> dicALLStates; //该控制器下的所有状态机
/// <summary>
/// 构造一个状态机控制器
/// </summary>
/// <param name="name">当前状态机控制器名称</param>
public C_FSMCtrl(string name)
{
//当前状态机控制器名称
ctrlName = name;
//实例化一个状态字典,用于存储所有的子状态
dicALLStates = new Dictionary<string, C_FSMState>();
//实例化一个根状态
rootState = new C_FSMState(rootStateName);
//如果所有的状态中不包含当前的根状态,则将其加入当所有状态中
if (!dicALLStates.ContainsKey(name))
{
//设置根状态的控制器为自身
rootState.fsmCtrl = this;
//将根状态加入到当前控制器状态集合中
dicALLStates.Add(name, rootState);
}
//将当前状态控制器加入到 所有状态控制 背包,该背包存储所有的状态控制器
C_FSMCtrlBags.AddFsmc(this);
}
/// <summary>
/// 为当前的状态控制器增加一个状态
/// </summary>
/// <param name="stateName">状态名称</param>
/// <param name="_act_start">状态开始时候的回调函数</param>
/// <param name="_act_over">状态结束时的回掉函数</param>
/// <param name="C_FSMState">返回增加的状态</param>
public C_FSMState AddState(string stateName,Action _act_start = null, Action _act_over = null, Action _act_stay = null,C_FSMState parentState = null)
{
//新建一个子状态
C_FSMState childState = null;
//如果当前状态控制器已经包含该子状态则自己取出该状态
if (dicALLStates.ContainsKey(stateName))
{
childState = dicALLStates[stateName];
}
else
{
//如果当前控制器不包含该子状态,则新建该状态
childState = new C_FSMState(stateName);
}
//更新它绑定的三个委托
childState.act_start = _act_start;
childState.act_over = _act_over;
childState.act_stay = _act_stay;
//若没有设置父节点,则默认为rootState节点
if (parentState == null)
{
parentState = rootState;
}
//如果当前状态的父状态存储子类状态的字典不存在
if (parentState.dicChilds == null)
{
//则实例化子类状态存储的容器
parentState.dicChilds = new Dictionary<string, C_FSMState>();
}
//如果当前状态的父状态不等于null则表明当前已经存在父状态,那么就相当于是要切换父状态,需将原先的父状态删除
if (childState.StateFather != null) // && childState.StateFather.dicChilds != null && childState.StateFather.dicChilds.ContainsKey(stateName))
{
childState.StateFather.dicChilds.Remove(stateName);
}
//指定当前状态的父状态
childState.StateFather = parentState;
//将当前状态的加入到父状态的子状态字典中
parentState.dicChilds.Add(childState.StateName, childState);
//设置子状态的路径
childState.path = parentState.path + "/" + childState.StateName;
//如果当前状态控制器不包含新状态的名称,则表示是新建状态,需要添加到字典中
//否则则表示新状态已经在控制器中了,说明是修改状态,修改后无需重复添加
if (!dicALLStates.ContainsKey(stateName))
{
//指定当前控制器为自身
childState.fsmCtrl = this;
//将新生成的状态追加到状态控制器字典中
dicALLStates.Add(stateName, childState);
}
return childState;
}
/// <summary>
/// 为当前的状态控制器增加一个状态
/// </summary>
/// <param name="stateName">状态名称</param>
/// <param name="parentStateName">指定父状态名称</param>
/// <param name="_act_start">状态开始时候的回调函数</param>
/// <param name="_act_over">状态结束时的回掉函数</param>
/// <param name="C_FSMState">返回增加的状态</param>
public C_FSMState AddState(string stateName, string parentStateName, Action _act_start = null, Action _act_over = null,Action _act_stay=null)
{
C_FSMState parentState = rootState;
//如果指定的当前父状态名称不等于默认的根状态名称,则表示当前的状态并不是要设置在根状态下
if (parentStateName != rootStateName)
{
//从当前状态机中取出需要设置的根状态
if (dicALLStates.ContainsKey(parentStateName))
{
parentState = dicALLStates[parentStateName];
}
else
{
//如果不存在则抛出异常,指定当前状态的父状态不存在
throw new System.Exception("名为:" + ctrlName + "的状态控制器不存在" + parentStateName + "索引");
}
}
return AddState(stateName, _act_start, _act_over, _act_stay, parentState);
}
//移除状态机,但是不销毁
public void RemoveState(string stateName)
{
//如果当前状态控制器中包含指定名称的状态
if (dicALLStates.ContainsKey(stateName))
{
//取出当前状态
C_FSMState state = dicALLStates[stateName];
//如果当前状态是激活状态,则调用over终止状态
if (state.isActivity)
{
state.over();
}
//递归回收该状态和所有子状态
RecursionStop(state);
}
}
//开始一个状态
public void StartState(string stateName)
{
//如果当前控制器中包含指定名称的状态
if (dicALLStates.ContainsKey(stateName))
{
//需要开始的状态
C_FSMState nextState = dicALLStates[stateName];
if (currentState != null) //如果控制器当前的状态不为空,则表示当前处于一个状态中,说明是切换状态
{
//需要开始的状态的父状态不为空,而且当前状态的父状态和需要开始的父状态相同,则表示同父是亲兄弟状态
if (nextState.StateFather != null && nextState.StateFather == currentState.StateFather)
{
//亲兄弟
//表示是兄弟状态允许切换,先终止当前状态,然后开始另外的状态
currentState.over();
nextState.start();
}
else //表示他们的父状态不同
{
/*
ROOT
| | | |
A B C D
| | | | | | |
E F G H I J K
假设当前状态是E,需要切换到H
1.找到他们相同的祖先(此处例子为ROOT),当前激活状态向上递归over()直到相同祖先(不包含相同祖先),切换后的状态递归start()直到相同祖先(不包含相同祖先),即
2.I状态over()
3.C状态over()
4.A状态start()
5.E状态start()
*/
//非亲兄弟, 则需要先回溯找到共同的父亲节点,然后切换,当前状态逐级向上调用over()
//1.找到公共父类节点
string[] arrPath1 = currentState.path.Split('/');
string[] arrPath2 = nextState.path.Split('/');
//防止索引越界,根据索引小遍历
int length = Math.Min(arrPath1.Length, arrPath2.Length);
//共同父状态名称
string togetherState = "";
//路径从根往下对比
//ROOT/A/E
//ROOT/C/I
//取得相同路径ROOT即为共同祖先
for (int i = 0; i < length; i++)
{
if (arrPath1[i] != arrPath2[i])
{
break;
}
else
{
togetherState = arrPath1[i];
}
}
/*由于FSMState中已经默认使用了递归开启和关闭,所以下面递归关闭就多余了,直接关闭和打开对应的节点即可
//将当前状态从下级往上级逐级关闭
for (int i = arrPath1.Length - 1; i >= 0; i--)
{
if (arrPath1[i] == togetherState)
{
break;
}
dicALLStates[arrPath1[i]].over();
}
//将需要新打开的状态从上级往下级逐级开启
bool isStart = false;
for (int i = 0; i < arrPath2.Length; i++)
{
if (togetherState == arrPath2[i])
{
isStart = true;
continue;
}
if (isStart)
{
dicALLStates[arrPath2[i]].start();
}
}
*/
//ROOT/C/I 之需要关闭C状态即可,默认会自动递归关闭I状态
bool selectNext = false;
for (int i = 0; i < arrPath1.Length; i++)
{
if (togetherState == arrPath1[i])
{
selectNext = true;
continue;
}
if (selectNext)
{
dicALLStates[arrPath1[i]].over();
break;
}
}
//ROOT/A/E 直接开启E状态即可,默认会自动递归开启A状态
dicALLStates[arrPath2[arrPath2.Length - 1]].start();
}
}
else //如果当前状态机并没有开启任何状态,则开始当前状态
{
nextState.start();
}
////将当前状态机指向新开启的状态机,无需指定,start()函数中,默认会绑定
//currentState = nextState;
}
else
{
//当需要开启的状态机不存在时
throw new Exception(ctrlName + "状态机字典中不存在对应的的状态" + stateName);
}
}
//停止状态
public void StopState(string stateName)
{
//如果当前状态存在,则关闭,默认会递归调用子类的状态进行关闭
if (dicALLStates.ContainsKey(stateName))
{
dicALLStates[stateName].over();
}
}
//直接递归销毁子类状态并且移除,不调用over
void RecursionStop(C_FSMState state)
{
//如果传入的状态的子状态不为空
if (state.dicChilds != null)
{
//则获取所有的子状态,仅包括子,不包括孙或更下一辈
List<C_FSMState> lstChilds = new List<C_FSMState>();
foreach (var item in state.dicChilds)
{
lstChilds.Add(item.Value);
}
//递归调用子类的
for (int i = 0; i < lstChilds.Count; i++)
{
RecursionStop(lstChilds[i]);
}
}
//如果当前状态存在父状态,则将当前状态从父状态中移除
if (state.StateFather != null)
{
state.StateFather.dicChilds.Remove(state.StateName);
}
//如果控制器中包含当前状态,则将当前状态从控制器中移除
if (dicALLStates.ContainsKey(state.StateName))
{
dicALLStates.Remove(state.StateName);
}
//清楚当前的状态的子字典,路径,父状态,父名称
state.dicChilds = null;
state.path = null;
state.StateFather = null;
state.StateName = "";
}
}
using System.Collections.Generic;
public class C_FSMCtrlBags{
//存储所有状态控制器的Bags
static Dictionary<string, C_FSMCtrl> dicFsmcs = new Dictionary<string, C_FSMCtrl>();
//根据字符串索引状态控制器
public C_FSMCtrl this[string indexStr]
{
get
{
//如果存在则返回,如果不存在则抛出异常
C_FSMCtrl res = null;
if (dicFsmcs.ContainsKey(indexStr))
{
res = dicFsmcs[indexStr];
}
else
{
throw new System.Exception("FSMCtrlBags不存在指定的索引"+ indexStr);
}
return res;
}
}
//将控制器加入到控制器背包中
public static void AddFsmc(C_FSMCtrl fsmc)
{
if (!dicFsmcs.ContainsKey(fsmc.ctrlName))
{
dicFsmcs.Add(fsmc.ctrlName, fsmc);
}
else
{
throw new System.Exception("FSMCtrlBags已存在key:" + fsmc.ctrlName);
}
}
}
网友评论