新FSM的一些思路
好久之前写过一篇关于状态机的小例子,可以看这里http://www.cnblogs.com/mawanli/p/5966080.html,这篇博客首先感谢需要感谢当时看到凉鞋的笔记博客,
凉鞋的博客地址先分享出来http://liangxiegame.com/tag/unity_framework/
今天在这里打算在重新谈论一下这些事情,是在一个gameframework的框架里面学到新的设计方法,今天打算是贡献出来,欢迎大家指教。
首先介绍下什么是状态机,状态机说白了就是自己的状态可以通过外界条件或者自身可以转换状态的一种模式。我们需要设计状态机和状态机的状态,每个状态包含进入此状态,离开此状态的事件,状态机里面需要记录当前的状态以及状态机里的事件和状态机调换状态的方法。
先看状态机的状态设计
public abstract class FSMState
{
private string m_StateName; /// <summary> /// 初始化有限状态机状态基类的新实例 /// </summary> /// <param name="name">状态名称</param> public FSMState(string name)
{
m_StateName = name;
} /// <summary> /// 状态的名字 /// </summary> public string GetStateName
{
get
{
return m_StateName;
}
} /// <summary> /// 有限状态机状态初始化时调用。 /// </summary> /// <param name="fsm">有限状态机引用。</param> public virtual void OnInit()
{ } /// <summary> /// 有限状态机状态进入时调用。 /// </summary> /// <param name="fsm">有限状态机引用。</param> public virtual void OnEnter()
{ } /// <summary> /// 有限状态机状态离开时调用。 /// </summary> /// <param name="fsm">有限状态机引用。</param> /// <param name="isShutdown">是否是关闭有限状态机时触发。</param> public virtual void OnLeave(bool isShutdown)
{ } /// <summary> /// 有限状态机状态销毁时调用。 /// </summary> /// <param name="fsm">有限状态机引用。</param> public virtual void OnDestroy()
{ }
}
接着是状态机的设计,状态机需要继承状态的的基类,先看基类的设计
public abstract class FSMBase<T,P>
{
private readonly string m_Name; /// <summary> /// 初始化有限状态机基类的新实例。 /// </summary> public FSMBase(): this(null)
{ } /// <summary> /// 初始化有限状态机基类的新实例。 /// </summary> /// <param name="name">有限状态机名称。</param> public FSMBase(string name)
{
m_Name = name ?? string.Empty;
} /// <summary> /// 获取有限状态机名称。 /// </summary> public string Name
{
get
{
return m_Name;
}
} /// <summary> /// 获取有限状态机持有者 /// </summary> public abstract T GetOwner
{
get;
} /// <summary> /// 获取有限状态机中状态的数量。 /// </summary> public abstract int FsmStateCount
{
get;
} /// <summary> /// 获取有限状态机是否正在运行。 /// </summary> public abstract bool IsRunning
{
get;
} /// <summary> /// 获取有限状态机是否被销毁。 /// </summary> public abstract bool IsDestroyed
{
get;
} /// <summary> /// 获取当前有限状态机状态名称。 /// </summary> public abstract string CurrentStateName
{
get;
} /// <summary> /// 关闭并清理有限状态机。 /// </summary> internal abstract void Shutdown();
}
接着看状态的设计
public sealed class FSM<T,P> : FSMBase<T,P>
{
private readonly Dictionary<P, FSMState> m_States; //记录所有状态机的状态 private readonly Dictionary<int, FsmEventHandler<FSMEventArgs>> m_EventHandler; //记录所有状态机中的事件 private FSMState m_CurrentState; //当前状态 private T m_Owner; //状态机持有者 private bool m_IsDestoryed; //是否被销毁 public FSM(string name, T owner, List<StateTypeData<P>> states): base(name)
{
if (owner == null)
{
Console.WriteLine("状态机持有者无效");
} if (states == null || states.Count < )
{
Console.WriteLine("状态机无效");
} m_Owner = owner;
m_States = new Dictionary<P, FSMState>();
m_EventHandler = new Dictionary<int, FsmEventHandler<FSMEventArgs>>(); foreach (StateTypeData<P> state in states)
{
if (state == null)
{
Console.WriteLine("状态机无效");
break;
}
if (m_States.ContainsKey(state.GetType))
{
Console.WriteLine("状态机中已经存在此状态"); } m_States.Add(state.GetType, state.GetState);
state.GetState.OnInit();
} m_CurrentState = null;
m_IsDestoryed = false;
} /// <summary> /// 状态机持有者 /// </summary> public override T GetOwner
{
get
{
return m_Owner;
}
} /// <summary> /// 状态的数量 /// </summary> public override int FsmStateCount
{
get
{
if (m_States != null)
{
return m_States.Count;
}
else
{
//状态机还未初始化 return ;
}
}
} /// <summary> /// 状态机是否在运行,在运行返回True /// </summary> public override bool IsRunning
{
get
{
return m_CurrentState != null;
}
} /// <summary> /// 状态机是否被销毁 /// </summary> public override bool IsDestroyed
{
get
{
return m_IsDestoryed;
}
} /// <summary> /// 状态名字 /// </summary> public override string CurrentStateName
{
get
{
if (m_CurrentState != null)
{
return m_CurrentState.GetStateName;
}
else
{
//取值无效 return null;
}
}
} public FSMState GetState()
{
return m_CurrentState;
} /// <summary> /// 关闭状态机 /// </summary> internal override void Shutdown()
{
if (m_CurrentState != null)
{
m_CurrentState.OnLeave(true);
m_CurrentState = null;
} foreach (KeyValuePair <P, FSMState> state in m_States)
{
state.Value.OnDestroy();
} m_States.Clear();
//m_Datas.Clear(); //状态机数据 m_IsDestoryed = true;
} /// <summary> /// 开启有限状态机 /// </summary> /// <param name="stateType">起始状态类型</param> public void Start(P stateType)
{
if (IsRunning)
{
//状态机已经在运行 }
FSMState state = GetState(stateType);
if (state == null)
{
//状态机起始状态不存在 }
m_CurrentState = state;
m_CurrentState.OnEnter();
} /// <summary> /// 状态机是否存在此状态 /// </summary> /// <param name="stateType"></param> /// <returns></returns> public bool HasState(P stateType)
{
return m_States.ContainsKey(stateType);
} /// <summary> /// 获取想要的状态 /// </summary> /// <param name="stateType"></param> /// <returns></returns> public FSMState GetState(P stateType)
{
FSMState state = null;
if (m_States.TryGetValue(stateType, out state))
{
return state;
}
return null;
} /// <summary> /// 是否存在此状态 /// </summary> /// <param name="name"></param> /// <returns></returns> public bool HasData(P name)
{
return m_States.ContainsKey(name);
} /// <summary> /// 切换当前有限状态机状态。 /// </summary> /// <param name="stateType">要切换到的有限状态机状态类型。</param> public void ChangeState(P stateType)
{
if (m_CurrentState == null)
{
//状态机已经失效 return;
}
FSMState state = GetState(stateType);
if (state == null)
{
//不能切换到不存在的状态 return;
}
m_CurrentState.OnLeave(false);
m_CurrentState = state;
m_CurrentState.OnEnter();
} /// <summary> /// 订阅有限状态机事件。 /// </summary> /// <param name="eventId">事件编号。</param> /// <param name="eventHandler">有限状态机事件响应函数。</param> public void SubscribeEvent(int eventId, FsmEventHandler<FSMEventArgs> eventHandler)
{
if (eventHandler == null)
{
//响应事件为空 } if (!m_EventHandler.ContainsKey(eventId))
{
m_EventHandler[eventId] = eventHandler;
}
else
{
m_EventHandler[eventId] += eventHandler;
}
} /// <summary> /// 取消订阅有限状态机事件。 /// </summary> /// <param name="eventId">事件编号。</param> /// <param name="eventHandler">有限状态机事件响应函数。</param> public void UnsubscribeEvent(int eventId, FsmEventHandler<FSMEventArgs> eventHandler)
{
if (eventHandler == null)
{
//事件无效 } if (m_EventHandler.ContainsKey(eventId))
{
m_EventHandler[eventId] -= eventHandler;
}
} /// <summary> /// 响应有限状态机事件时调用。 /// </summary> /// <param name="fsm">有限状态机引用。</param> /// <param name="sender">事件源。</param> /// <param name="eventId">事件编号。</param> /// <param name="userData">用户自定义数据。</param> public void OnEvent(object sender, int eventId, FSMEventArgs userData)
{
FsmEventHandler<FSMEventArgs> eventHandlers = null;
if (m_EventHandler.TryGetValue(eventId, out eventHandlers))
{
if (eventHandlers != null)
{
eventHandlers(sender, userData);
}
}
}
}
状态机里的事件设计,,所有事件都要继承此类
public abstract class FSMEventArgs: EventArgs
{
private String m_Name; /// <summary> /// 初始化事件类 /// </summary> /// <param name="_name">事件名字</param> public FSMEventArgs(String _name)
{
m_Name = _name;
} /// <summary> /// 得到事件的名字 /// </summary> public String GetEventName
{
get
{
return m_Name;
}
}
}
/// <summary> /// 状态机事件 /// </summary> /// <typeparam name="P">状态机的枚举</typeparam> /// <param name="sender">发送者</param> /// <param name="userData">发送的数据</param> public delegate void FsmEventHandler<P>(object sender, P userData) where P : FSMEventArgs;
下面我自己写的三个状态的状态机的例子
首先定义了状态的枚举(方便区分状态,切换状态等都使用此枚举)
/// <summary> /// 状态机枚举 /// </summary> public enum ActioEnum
{
Start,
Update,
LateUpdate,
DisViable,
End
}
接着是几个状态,
public class StateA : FSMState
{
public StateA() : base(typeof(StateA).FullName)
{
} public override void OnDestroy()
{
base.OnDestroy();
Console.WriteLine("StateA销毁");
} public override void OnEnter()
{
base.OnEnter();
Console.WriteLine("StateA进入");
} public override void OnInit()
{
base.OnInit();
Console.WriteLine("StateA初始化");
} public override void OnLeave(bool isShutdown)
{
base.OnLeave(isShutdown);
Console.WriteLine("StateA离开");
}
}
这是状态A,还有状态A,C, 大家可以根据状态A进行类比B,C,这里就不做出示例
状态机的大家可以参考下面的示例
void Start ()
{
foreach (BianLiEnum item in Enum.GetValues(typeof(BianLiEnum)))
{
BianLiEnum q = (BianLiEnum)((int)item);
} List<StateTypeData<ActioEnum>> fSMStateList = new List<StateTypeData<ActioEnum>>();
StateA stateA = new StateA();
StateTypeData<ActioEnum> stA = new StateTypeData<ActioEnum>(ActioEnum.Start, stateA); StateB stateB = new StateB();
StateTypeData<ActioEnum> stB = new StateTypeData<ActioEnum>(ActioEnum.Update, stateB); StateC stateC = new StateC();
StateTypeData<ActioEnum> stC = new StateTypeData<ActioEnum>(ActioEnum.LateUpdate, stateC); fSMStateList.Add(stA);
fSMStateList.Add(stB);
fSMStateList.Add(stC); UnityGameObject unityObj = new UnityGameObject();
FSM<UnityGameObject, ActioEnum> m_FSM = new FSM<UnityGameObject, ActioEnum>("第一个状态机", unityObj, fSMStateList);
m_FSM.Start(ActioEnum.Start);
Debug.Log("状态机持有者是" + m_FSM.GetOwner);
m_FSM.ChangeState(ActioEnum.Update);
m_FSM.Shutdown();
Debug.Log("状态机已经销毁");
}
大家会发现示例当中创建状态机很繁琐,
然后我就用反射去创建状态机,下面是我通过反射创建得到所有的子类
public static List<T> GetTypeChilds<T>(Type parentType)
{
List<T> lstType = new List<T>();
List<Type> typeList = new List<Type>();
List<string> typeNameList = new List<string>();
List<string> typeNameTempList = new List<string>();
Assembly assem = Assembly.GetAssembly(parentType);
foreach (Type tChild in assem.GetTypes())
{
if (tChild.BaseType == parentType)
{
typeList.Add(tChild);
typeNameList.Add(tChild.FullName);
typeNameTempList.Add(tChild.FullName);
//lstType.Add((T)Activator.CreateInstance(tChild)); }
} typeNameTempList.Sort();
int listLength = typeList.Count;
int nowIndex = ;
for (int i = ; i < listLength; i++)
{
nowIndex = typeNameList.IndexOf(typeNameTempList[i]);
lstType.Add((T)Activator.CreateInstance(typeList[nowIndex]));
} return lstType;
}
这只是得到所有的子类,没有添加到状态机当中去,还需要进行完善
下次修改的时候把反射加入到状态机的创建当中去,代码就会瞬间清晰许多
新FSM的一些思路的更多相关文章
- 【Android】一种提高Android应用进程存活率新方法
[Android]一种提高Android应用进程存活率新方法 SkySeraph Jun. 19st 2016 Email:skyseraph00@163.com 更多精彩请直接访问SkySeraph ...
- 一种提高Android应用进程存活率新方法
一.基础知识 1.Android 进程优先级 1.1 进程优先级等级一般分法:- Activte process- Visible Process- Service process- Backgrou ...
- Service系统服务(一):安装一个KVM服务器、KVM平台构建及简单管理、virsh基本管理操作、xml配置文件的应用、为虚拟机制作快照备份、快建新虚拟机
一.安装一个KVM服务器 目标: 本例要求准备一台 RHEL7.2 服务器,将其搭建为KVM平台,主要完成下列操作: 1> 关闭本机的SELinux保护.防火墙服务 2> 挂载RHEL ...
- MySQL新特性MTS
一.MTS:多线程复制 MTS简介 在MySQL 5.6版本之前,Slave服务器上有两个线程I/O线程和SQL Thread线程.I/O线程负责接收二进制日志(Binary Log,更准确的说是二进 ...
- 9、链表 & 状态机 & 多线程
链表的引入 从数组的缺陷说起 数组有2个缺陷:一个是数组中所有元素的类型必须一致:第二个是数组的元素个数必须事先制定并且一旦指定之后不能更改. 如何解决数组的2个缺陷:数组的第一个缺陷靠结构体去解决. ...
- Xamarin Android 应用程序内图标上数字提示
最近在用 Xamarin 做一个 Android 应用,打开应用时,如果有新消息,需要在应用内的 Toolbar 或者首页的图标上显示数字提示.在这里和大家分享一下实现方法,如果你有更新好的实现方法, ...
- C# 开发windows服务的一些心得
最近在做一个windows服务的项目,发现并解决了一些问题,拿出来和大家分享一下,以下windows服务简称“服务” 文章会在适合时间更新,因为朋友们在不断提出新的意见或思路,感谢-.- 1.服务如何 ...
- When it comes to intrusion analysis and forensics
以下内容的出现可以追溯到一个发生在互联网的安全事件: Z公司遭受某种攻击,服务器上被植入了Linux DDOS木马,部分系统命令入ls遭替换,攻击者已经获得该服务器root权限: 影响更恶劣的是,连接 ...
- Redis初识、设计思想与一些学习资源推荐
一.Redis简介 1.什么是Redis Redis 是一个开源的使用ANSI C 语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value 数据库,并提供多种语言的API.从2010 年 ...
随机推荐
- 画布Canvas 画笔Paint
package com.example.m_evolution.View; import android.content.Context; import android.graphics.Canvas ...
- JS——按钮点击事件累加注册问题
最近在工作上遇到一个点击事件累加的问题,为元素添加点击事件效果,但是总是效果失败,最后发现点击事件被执行了多次,上网查了一下,下边就是解决这个问题的几种思路 案列引自 踮起脚尖眺望6 $(" ...
- Linux debug
proc文件系统中可以查看一些正在运行的变量如device-tree sh-3.2# cat /proc/device-tree/ #address-cells fixedregulator@9/ # ...
- IDEA中,将项目加入maven管理。
在项目上右键->Add Framework Support Choose Maven 生成pom.xml 在<project>下配置国内仓库 <properties>&l ...
- SpringMVC避免IE执行AJAX,返回JSON出现下载文件
- Linux 学习笔记 2:文件系统
1.文件系统层次结构 系统目录内容: /: 根目录(之后的/都是目录分隔符) /home:用户目录 /bin: Unix常用命令,如bash, date, cat, tar等 /sbin: 管理员命令 ...
- appium定位toast消息的使用
定位使用xpath后,定位消息文本,然后使用text获取消息文本做断言.toast_loc = ("xpath", ".//*[contains(@text,'切换运营商 ...
- SystemUI中监听app启动,修改app中的状态栏背景色
参考 http://www.2cto.com/kf/201206/137225.html 从Android4.4开始,app可以自定义status bar 背景. 对于一些第三方app定义的状态栏背景 ...
- asp.net core web 项目附加进程调试
之前asp.net web项目在部署IIS站点的时候可以直接选择项目目录,不用发布,然后附加进程的时候,找到w3wp.exe开头的进程,再根据用户名找到要附加的进程,就可以附加进程调试了.但asp.n ...
- 绑定hover事件
<label> <span id="pattern">实战模式</span> <div class='tab' style="t ...