有限状态机机制介绍及在Android中应用的实例

上来可能有疑问
什么是状态机?能干什么?

第一次听说状态机 是从游戏开发技术中得来的。在游戏开发中,可以使用状态机控制npc的行为,控制角色在 站立,追踪敌人,攻击敌人,逃跑等状态进行切换。每一个状态都会执行该状态进行时执行的一系列操作。

简而言之,就是控制对象状态的切换。

用图示来表示一下

这是一个游戏demo,注意敌人的各种行为

在这里插入图片描述
敌人没有发现玩家时,呆立不动,这时,敌人可以说是处于 “空闲” 的状态

在这里插入图片描述
当敌人发现玩家,会向玩家靠近,这时,可以说敌人处于 “追击” 状态

在这里插入图片描述
当敌人追上玩家,就会对玩家进行攻击,这时,可以说敌人处于 “攻击” 状态。

在这里插入图片描述
当然 玩家不是吃素的,当玩家反击并击败敌人,这时 敌人说敌人处于 “死亡” 状态。

在这里插入图片描述
当然 ,玩家可以不选择击败敌人,也可以战斗中逃离,当玩家逃出一定视野范围,npc无法看到玩家,则npc 又可以回到 “空闲” 状态。

注意到,NPC在某个状态中,还需要进行各种行为,例如 “空闲”状态时,会不停的 东张西望 。 “追击” 状态时,会朝向敌人奔跑。“攻击”状态时,会向玩家挥动武器。 “死亡”状态时 会倒地不起。

以上的这些行为是文字描述的,是游戏策划设计的。

作为程序员,是有职责将上述的设计转换成代码实现的。

那么,代码将如何实现这些设计呢?可能有人会想,那么我就设计一个控制类,里面有许多控制变量,通过判断控制变量来进行控制,例如下面伪代码

if(distance(player,npc)<SEE_DISTANCD){
   
	npc.move(player)
}
if(distance(player,npc)<ATTACK_DISTANCD){
   
	mpc.attack(player)
}
if(npc.health<=0){
   
	mpc.death()
}
if(distance(player,npc)>=SEE_DISTANCD)){
   
	npc.idle();
}
...

通过一定数量的if-else来控制这些行为。然鹅,实际情况下这种做法是不可行的,因为实际的状态转换条件有很多种,必须进行if-else嵌套才能完成所有的逻辑,如果写全这些逻辑的话,代码是不可维护的(不必怀疑,自己试一下便知)。

这次的主角要登场了 这就是有限状态机(finite state machine)简称为FSM。

在介绍状态机之前,我们要用一个图示来表示上述游戏中的NPC的各种状态之间的关系。
在这里插入图片描述
图中,蓝色框代表有限的所有的状态,绿色有向箭头代表状态间可以进行切换的条件 ,整个图示叫做状态图

这个图非常重要。在编写状态机代码前,必须先画出这个状态图。

状态机 可以理解为一台不停工作的机器,运行一段循环执行的代码,来控制状态间的切换和处于每个状态期间需要执行的操作

一个状态机的接口是这样的

interface IFSM{
   
	update();
	addState(State state);
	changeState(int stateID);
	setupFSM();
}

其中 update()是系统提供的更新机制(例如Unity中的 update()方法,Android 中的 Handle.postDelay())
每当达到更新时机时,通知状态机更新当前状态,也即是调用状态机的update()。
更新当前状态包括 检查当前状态是否满足条件,查询另一个满足条件的状态,切换当前状态到应该到达的状态,每当update()时,就完成了状态图的一个蓝框到另一个蓝框的过程,当然状态没有变化则继续处于当前的蓝框状态。

addState()提供了一个交由状态机管理的所有状态的添加功能,状态机本质上是管理了所有的状态,并把某一个设置为当前状态

再回到代码层面

实现状态机,还有两个类必不可少
状态类
代表状态图中的蓝框,职责是处于某种状态时,执行状态中的操作,比如 攻击玩家 或者 寻找玩家位置,向玩家前进。
一个状态类的接口是这样的

interface IState{
   
	void check(IFSM fsm);
	void onEnter(IFSM fsm);
	void onAction(IFSM fsm);
	void onExit(IFSM fsm);
}

其中,
onEnter()提供了一个刚进入状态中执行的一次性操作 比如 开始循环播放空闲动画。
onAction()提供了一个处于状态中需要持续进行的操作 比如 攻击玩家。
onExit()提供了一个状态转换时本状态退出时需要进行的操作 比如 停止攻击。
check()提供了当当前状态已经不满足条件时,通过满足条件的条件编号找到本状态可以切换的下一个条件,来交给状态机设置为当前状态
函数参数中都有一个状态机的引用,是方便再各种操作时,能方便访问到状态机管理的外部引用(例如player 玩家,npc 等)

以上是状态类,接下来是最后一个关键类
条件类条件类代表了状态图中的的绿色箭头,职责是判断对应的状态是否满足,比如追击这个条件是否满足,如果满足则返回true。

条件类的接口如下所示

interface ITrigger{
   
	boolean checkTrigger(IFSM fsm);
}

我们把条件类命名为Trigger 因为条件如果满足的话,说明会触发一次条件的转换。所以相当于条件转换是由条件类触发的,所以叫Trigger.

checkTrigger()提供了一个判断某个状态对应的条件是否满足的依据,例如攻击状态对应的条件攻击条件中,checkTrigger()应该如下所示

AttackTrigger implements ITrigger{
   
	@Override
	boolean checkTrigger(IFSM fsm){
   
		return distance(fsm.getPlayer(),fsm.getNpc())<=ATTACK_DISTANCE;
	}
}

这里,大家看到了最初庞大的的if-else语句中的某些代码了。这里体现了状态机机制相比大块if-else逻辑的一个优点,就是单一职责原则,每个条件通过一个类来负责,各条件之负责本条件对应的判断。
状态类的方法中也有一个状态机参数,同样是为了方便访问到公用的数据(这里是游戏对象player及npc)。

让我们把 状态机类,条件类,状态类整合起来,让整个系统运行起来吧。

到这里,相信大部分人还是不懂状态机是怎么运作的,能控制NPC做出上面的反应。这就需要将上述的重要的类整合起来了。

总体来说,上述的类放在一起是这样的
在这里插入图片描述
各类之间是包含的关系。

首先 状态机有一个状态列表,保存了管理的所有的有限的状态。

状态机还负责切换状态,切换状态的意思是 把某一个新状态设为当前状态。

每一个状态类都管理了一个条件类列表,代表该状态能够到达的其他状态应该满足的条件
还包括了一个条件-状态映射 一般使用Map来表示。映射是当某一个条件满足

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值