本文章参考自Unity3D人工智能编程精粹,转载请注明出处。
有限状态机的FSM图
有限状态机(FSM)由一组状态(包括一个初始状态)、输入和根据输入及现有状态转换为下一个状态的转换函数组成。
什么是状态?
飞翔,行走,攻击,跑步,游泳,这些动词是状态。高兴,伤心,生气这些形容词也是状态,一些名词也能表示状态。状态的关键意义是:不同的状态对应不同的行为。
关于有限状态机(FSM),需要了解以下几点:
1. 有限状态机是AI系统中最简单的,同时也是最为有效和最常用的方法。对于游戏中的每个对象,都可以在其生命周期内区分出一些状态。
2. 当某些条件发生时,状态机从当前状态转换为其他状态。在不同的状态下,游戏对象对外部激励做出不同的反应或执行不同的动作。有限状态机方法让我们可以很容易地把游戏对象的行为划分为小块,这样更容易调试和扩展。
3. 用户编写的每个程序都是状态机。当没写下一个if语句的时候,就创造出了一段至少拥有两个状态的代码——写的代码越多,程序就可能具有越多的状态。Switch和if语句数量的爆发会让事情很快失去控制,程序会出现奇怪的BUG。
4. 有限状态机是AI中最容易的部分,但也很容易出错。在设计有限状态机的时候,一定要认真地考虑清楚其中的每个状态和转换部分。
简易的FSM图
有限状态机的状态转移矩阵
状态转移矩阵是简介表明FSM图的转移矩阵,分别有当前状态,输入,输出状态。如上图的状态转移矩阵可以为
用FSM框架实现通用的有限状态机
介绍一个通用的FSM框架,可以在unitycommunity.com上找到,地址是http://wiki.unity3d.com/index/php?title=Finite_State_Machine。
其实仔细想想,这个FSM框架是不是跟Animator动画状态机非常像,对!动画状态机、有限状态机,你没看错。但是我们还是得会写才是王道!
上面的状态UML图为
FSM: AdvancedFSM的父类,继承MonoBehaviour,封装了其中的Start,Update,FixedUpdate方法。定义了巡逻数组等。
AdvanceFSM: 管理所有的状态类,FSM的派生类,负责管理FSMState的派生类,并且随着当前状态和输入,进行状态更新。
FSMState: 所有状态的基类,状态类中具有添加转换,删除转换的方法,用于管理记录这些转换。在FSMState中有一个字典对象,用来存储 “转换—状态” 对,表明在当前状态(即这个类所代表的状态)下,如果发生某个 “转换” ,FSM将会转移到何种状态。可以通过AddTransition方法和DeleteTransition方法(自己写)添加或删除 “转换—状态” 对。另外这个类中还包括Reason方法和Act方法,Reason用来确定是否需要转换状态。Act方法定义了在本状态角色行为,例如移动,动画等。
AICotroller: AdvanceFSM的派生类,负责创建有限状态机,通过它来控制角色。
代码如下:
FSM.cs
public class FSM : MonoBehaviour { ////// 玩家Transfrom组件 /// protected Transform playerTransform; ////// 下一个目标 /// protected Vector3 destPos; ////// 攻击速率 /// protected float attackRate; ////// 攻击间隔 /// protected float elapsedTime; ////// 初始化 /// protected virtual void Init() { } ////// Update /// protected virtual void FSMUpdate() { } ////// FixedUpdate /// protected virtual void FSMFixedUpdate() { } void Start() { Init(); } void Update() { FSMUpdate(); } void FixedUpdate() { FSMFixedUpdate(); }}
AdvanceFSM.cs
public class AdvanceFSM : FSM { private ListfsmStates; private FSMStateID currentStateID; public FSMStateID CurrentStateID { get { return currentStateID; } } private FSMState currentState; public FSMState CurrentState { get { return currentState; } } public AdvanceFSM() { fsmStates = new List (); } /// /// 添加状态 /// /// public void AddFSMState(FSMState fsmState) { if (fsmState == null) { return; } if (fsmStates.Count == 0) { fsmStates.Add(fsmState); currentState = fsmState; currentStateID = fsmState.ID; return; } foreach (FSMState state in fsmStates) { if (state.ID == fsmState.ID) { return; } } fsmStates.Add(fsmState); } ////// 删除状态 /// /// public void DelFSMState(FSMStateID fsmState) { foreach (FSMState state in fsmStates) { if (state.ID == fsmState) { fsmStates.Remove(state); return; } } } ////// 转移新状态 /// /// public void PerformTransition(Transition transition) { FSMStateID id = currentState.GetOutputState(transition); currentStateID = id; foreach (FSMState state in fsmStates) { if (state.ID == currentStateID) { currentState = state; return; } } }}public enum Transition{ ////// 看到玩家 /// SawPlayer = 0, ////// 接近玩家 /// ReachPlayer, ////// 玩家离开视线 /// LostPlayer, ////// 被伤害 /// Injured, ////// 死亡 /// NoHealth,}public enum FSMStateID{ ////// 巡逻状态 /// Idling = 0, ////// 追逐状态 /// Chasing, ////// 攻击状态 /// Attacking, ////// 受伤状态 /// Injuring, ////// 死亡状态 /// Dead,}
FSMState.cs
public abstract class FSMState{ ////// 转换-状态,信息字典 /// protected Dictionarymap = new Dictionary (); /// /// 状态编号ID /// protected FSMStateID stateID; ////// ID公开 /// public FSMStateID ID { get { return stateID; } } ////// 目标位置 /// protected Vector3 destPos; ////// 转向速度 /// protected float rotSpeed; ////// 移动速度 /// protected float speed; ////// 追逐距离 /// protected float chaseDistance = 7.0f; ////// 攻击距离 /// protected float attackDistance = 1.4f; ////// 到达距离 /// protected float arriveDistance = 1.4f; ////// 添加“转换-状态” /// /// /// public void AddTransition(Transition transition, FSMStateID id) { if (map.ContainsKey(transition)) { Debug.LogWarning("GG"); } map.Add(transition, id); } ////// 删除“转换-状态” /// /// public void DelTransition(Transition transition) { if (map.ContainsKey(transition)) { map.Remove(transition); } } ////// 获取转换后新状态编号 /// /// ///public FSMStateID GetOutputState(Transition transition) { Debug.Log(transition); return map[transition]; } /// /// 是否需要转换,如何转换 /// /// /// public abstract void Reason(Transform player, Transform monster); ////// 角色行为 /// /// /// public abstract void Act(Transform player, Transform monster);}
AIController.cs
public class AIController : AdvanceFSM { ////// 角色生命值 /// private int health; ////// 初始化AI角色的FSM,在FSM基类的Start函数中调用。 /// protected override void Init() { health = 100; Blood.Instance.Change(health); elapsedTime = 0.0f; attackRate = 2.0f; GameObject objPlayer = GameObject.FindGameObjectWithTag("Player"); playerTransform = objPlayer.transform; if (!playerTransform) print("GG"); ConstructFSM(); } ////// 这个函数在初始化Init方法中调用,为AI角色构造FSM。 /// private void ConstructFSM() { IdleState idle = new IdleState(); idle.AddTransition(Transition.SawPlayer, FSMStateID.Chasing); idle.AddTransition(Transition.Injured, FSMStateID.Injuring); idle.AddTransition(Transition.NoHealth, FSMStateID.Dead); ChaseState chase = new ChaseState(); chase.AddTransition(Transition.LostPlayer, FSMStateID.Idling); chase.AddTransition(Transition.ReachPlayer, FSMStateID.Attacking); chase.AddTransition(Transition.Injured, FSMStateID.Injuring); chase.AddTransition(Transition.NoHealth, FSMStateID.Dead); AttackState attack = new AttackState(); attack.AddTransition(Transition.LostPlayer, FSMStateID.Idling); attack.AddTransition(Transition.SawPlayer, FSMStateID.Chasing); attack.AddTransition(Transition.Injured, FSMStateID.Injuring); attack.AddTransition(Transition.NoHealth, FSMStateID.Dead); InjuryState injury = new InjuryState(); injury.AddTransition(Transition.LostPlayer, FSMStateID.Idling); injury.AddTransition(Transition.SawPlayer, FSMStateID.Chasing); injury.AddTransition(Transition.NoHealth, FSMStateID.Dead); DeadState dead = new DeadState(); dead.AddTransition(Transition.NoHealth, FSMStateID.Dead); AddFSMState(idle); AddFSMState(chase); AddFSMState(attack); AddFSMState(injury); AddFSMState(dead); } protected override void FSMUpdate() { elapsedTime += Time.deltaTime; } protected override void FSMFixedUpdate() { CurrentState.Act(playerTransform, transform); CurrentState.Reason(playerTransform, transform); } ////// 这个方法在每个状态类的Reason中被调用。 /// /// public void SetTransition(Transition t) { PerformTransition(t); } ////// AI角色与其他物体碰撞时,调用这个函数。 /// /// void OnTriggerEnter(Collider collider) { if (collider.gameObject.tag.Equals("Player")) { health -= 40; Blood.Instance.Change(health); if (health <= 0) { SetTransition(Transition.NoHealth); } else { SetTransition(Transition.Injured); } } } public void Attack(Animator anim) { if (elapsedTime >= attackRate) { anim.SetTrigger("Surround Attack"); elapsedTime = 0f; } } public void Injury(Animator anim) { anim.SetTrigger("Take Damage"); }}
IdleState.cs(没有做随机移动,用站立表示默认状态)
public class IdleState : FSMState{ ////// 初始化状态 /// public IdleState() { stateID = FSMStateID.Idling; rotSpeed = 6.0f; speed = 10.0f; } ////// 播放动画 /// /// /// public override void Act(Transform player, Transform monster) { Animator anim = monster.GetComponent(); } /// /// 是否进行状态转移 /// /// /// public override void Reason(Transform player, Transform monster) { //如果玩家与怪物距离小于等于追逐距离,那么转移状态为追逐 if (Vector3.Distance(player.position,monster.position) <= chaseDistance) { monster.GetComponent().SetTransition(Transition.SawPlayer); } }}