好久之前写过一篇关于状态机的小例子,可以看这里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的一些思路的更多相关文章

  1. 【Android】一种提高Android应用进程存活率新方法

    [Android]一种提高Android应用进程存活率新方法 SkySeraph Jun. 19st 2016 Email:skyseraph00@163.com 更多精彩请直接访问SkySeraph ...

  2. 一种提高Android应用进程存活率新方法

    一.基础知识 1.Android 进程优先级 1.1 进程优先级等级一般分法:- Activte process- Visible Process- Service process- Backgrou ...

  3. Service系统服务(一):安装一个KVM服务器、KVM平台构建及简单管理、virsh基本管理操作、xml配置文件的应用、为虚拟机制作快照备份、快建新虚拟机

    一.安装一个KVM服务器 目标: 本例要求准备一台 RHEL7.2 服务器,将其搭建为KVM平台,主要完成下列操作: 1> 关闭本机的SELinux保护.防火墙服务   2> 挂载RHEL ...

  4. MySQL新特性MTS

    一.MTS:多线程复制 MTS简介 在MySQL 5.6版本之前,Slave服务器上有两个线程I/O线程和SQL Thread线程.I/O线程负责接收二进制日志(Binary Log,更准确的说是二进 ...

  5. 9、链表 & 状态机 & 多线程

    链表的引入 从数组的缺陷说起 数组有2个缺陷:一个是数组中所有元素的类型必须一致:第二个是数组的元素个数必须事先制定并且一旦指定之后不能更改. 如何解决数组的2个缺陷:数组的第一个缺陷靠结构体去解决. ...

  6. Xamarin Android 应用程序内图标上数字提示

    最近在用 Xamarin 做一个 Android 应用,打开应用时,如果有新消息,需要在应用内的 Toolbar 或者首页的图标上显示数字提示.在这里和大家分享一下实现方法,如果你有更新好的实现方法, ...

  7. C# 开发windows服务的一些心得

    最近在做一个windows服务的项目,发现并解决了一些问题,拿出来和大家分享一下,以下windows服务简称“服务” 文章会在适合时间更新,因为朋友们在不断提出新的意见或思路,感谢-.- 1.服务如何 ...

  8. When it comes to intrusion analysis and forensics

    以下内容的出现可以追溯到一个发生在互联网的安全事件: Z公司遭受某种攻击,服务器上被植入了Linux DDOS木马,部分系统命令入ls遭替换,攻击者已经获得该服务器root权限: 影响更恶劣的是,连接 ...

  9. Redis初识、设计思想与一些学习资源推荐

    一.Redis简介 1.什么是Redis Redis 是一个开源的使用ANSI C 语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value 数据库,并提供多种语言的API.从2010 年 ...

随机推荐

  1. 针对特定网站scrapy爬虫的性能优化

    在使用scrapy爬虫做性能优化时,一定要根据不同网站的特点来进行优化,不要使用一种固定的模式去爬取一个网站,这个是真理,以下是对58同城的爬取优化策略: 一.先来分析一下影响scrapy性能的set ...

  2. pycrypto 安装 Crypto 报错 error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": http://landinghub.visualstudio.com/visual-cpp-build-tools

    error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools&quo ...

  3. vue slot 复用

    话不投机半句多,直接上代码 有3步 第一步:创建渲染slot的组件 重要 第二步:为slot添加父组件数据(props) 重要 第三步:使用 第一步:创建渲染slot的组件 首选创建一个单文件组价,由 ...

  4. skynet记录7:服务(c和lua)

    稍后填坑 1.c服务的写法(第一个服务logger分析) 2.lua服务的写法(第二个服务bootstrap分析) 3.snlua包装模块

  5. Sketch 画原型比 Axure 好用吗?为什么?

    对工具而言,个人觉得没有说哪个工具好用不好用之分,更重要一点,做设计的来讲什么时候用什么工具来提高工作效率,这个最重要.下面我也来讲讲这二款工具的不同之处: Axure算是原型工具里的 Old Sch ...

  6. java 多线程的唤醒

    package TestThread.ThreadSynchronized.TestInterruptedException; public class InterruptDemo { public ...

  7. 2-创建spring boot项目

    想要创建一个spring boot项目,最好的方法就是参照官网的例子: https://spring.io/guides/gs/maven/#scratch 创建的过程我就不再啰嗦了,官网描述非常详细 ...

  8. guns开源项目数据库切换为oracle

    本次使用oracle版本 11.2.0.1.0 1.guns-core 修改pom.xml 文件引入oracle驱动 <dependency> <groupId>com.ora ...

  9. 低版本的Chrome,打开url时,报错,IE确可以打开;

    解决办法:打开注册表,添加以下内容,之后重启服务器: [HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\HTTP\Parameters]Ena ...

  10. angular 实现 echarts 拖动区域进行放大 方法

    实现逻辑: 1.通过鼠标摁下事件  和弹出事件  获取x轴的index  之后去x轴的list中去获取两个坐标点 2.之后将这两个数据作为参数  传到后台更新数据 3.记录下来这两个坐标点 放到lis ...