有限状态机,(英语:Finite-state machine, FSM),又称有限状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。状态存储关于过去的信息,它反映从系统开始到现在时刻输入的变化;转移指示状态变更,用必须满足来确使转移发生的条件来描述它;动作是在给定时刻要进行的活动描述。有多种类型的动作:
进入动作(entry action):在进入状态时进行;
退出动作:在退出状态时进行;
输入动作:依赖于当前状态和输入条件进行;
转移动作:在特定转移时进行。
游戏引擎是有限状态机最为成功的应用领域之一,由于设计良好的状态机能够被用来取代部分的人工智能算法,因此游戏中的每个角色或者器件都有可能内嵌一个状态机。考虑RPG游戏中城门这样一个简单的对象,它具有打开(Opened)、关闭(Closed)、上锁(Locked)、解锁(Unlocked)四种状态,如图1所示。当玩家到达一个处于状态Locked的门时,如果此时他已经找到了用来开门的钥匙,那么他就可以利用它将门的当前状态转变为Unlocked,进一步还可以通过旋转门上的把手将其状态转变为Opened,从而成功地进入城内。
在描述有限状态机时,状态、事件、转换和动作是经常会碰到的几个基本概念。
状态(State)指的是对象在其生命周期中的一种状况,处于某个特定状态中的对象必然会满足某些条件、执行某些动作或者是等待某些事件。
事件(Event)指的是在时间和空间上占有一定位置,并且对状态机来讲是有意义的那些事情。事件通常会引起状态的变迁,促使状态机从一种状态切换到另一种状态。
转换(Transition)指的是两个状态之间的一种关系,表明对象将在第一个状态中执行一定的动作,并将在某个事件发生同时某个特定条件满足时进入第二个状态。
动作(Action)指的是状态机中可以执行的那些原子操作,所谓原子操作指的是它们在运行的过程中不能被其他消息所中断,必须一直执行下去。
创建状态机的基类
“`
using UnityEngine;
using System.Collections;
public interface Istate {
uint GetStateId();//获取状态id
void EnterState(Istate preState);//进入状态
void LeaveState(Istate nextState);//离开状态
void OnUpdate();//更新状态
void OnFixedUpdate();//更新状态
void OnLateUpdate();//更新状态
}
创建状态管理类
using UnityEngine;
using System.Collections;public class PatrolState : Istate
{
Transform player;
Transform enemy;
Transform[] paths;
Animator anim;
CharacterController cc;
int pathIndex = 0;
public PatrolState(Transform player, Transform enemy, Transform[] paths)
{
this.paths = paths;
this.player = player;
this.enemy = enemy;
anim = enemy.GetComponent();
cc = enemy.GetComponent();}
public uint GetStateId()
{//获取状态id
return (uint)EnemyStateId.PatrolState;
}
public void EnterState(Istate preState)
{//设置动画为运动
anim.SetBool("Idel", false);
}
public void LeaveState(Istate nextState)
{
}
public void OnUpdate()
{//控制移动和移动方向
enemy.LookAt(paths[pathIndex]);
cc.SimpleMove(enemy.transform.forward * 3f);
if (Vector3.Distance(enemy.position, paths[pathIndex].position) < 1)
{//当敌人与下一个节点距离小于1,转换节点
pathIndex++;
if (pathIndex >= paths.Length)
{
pathIndex = 0;
}
}
//当敌人与玩家距离小于15,切换为追击玩家状态
if (Vector3.Distance(enemy.position, player.position) < 15)
{
enemy.GetComponent<EnemyCtrl>().SwitchState((uint)EnemyStateId.ChaseState);
}
}
public void OnFixedUpdate()
{
}
public void OnLateUpdate()
{
}
}
public uint GetStateId()
{//获取状态id
return (uint)EnemyStateId.PatrolState;
}
public void EnterState(Istate preState)
{//设置动画为运动
anim.SetBool("Idel", false);
}
public void LeaveState(Istate nextState)
{
}
public void OnUpdate()
{//控制移动和移动方向
enemy.LookAt(paths[pathIndex]);
cc.SimpleMove(enemy.transform.forward * 3f);
if (Vector3.Distance(enemy.position, paths[pathIndex].position) < 1)
{//当敌人与下一个节点距离小于1,转换节点
pathIndex++;
if (pathIndex >= paths.Length)
{
pathIndex = 0;
}
}
//当敌人与玩家距离小于15,切换为追击玩家状态
if (Vector3.Distance(enemy.position, player.position) < 15)
{
enemy.GetComponent<EnemyCtrl>().SwitchState((uint)EnemyStateId.ChaseState);
}
}
public void OnFixedUpdate()
{
}
public void OnLateUpdate()
{
}
}
“`
创建状态管理类
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class StateMachine
{
Dictionary<uint, Istate> stateDic = new Dictionary<uint, Istate>();
Istate currentState;
uint currentStateId;
public Istate CurrentState
{//获取当前状态
get { return currentState; }
}
public uint CurrentStateId
{//获取当前状态id
get { return currentState.GetStateId(); }
}
public void RegisterState(Istate state)
{//如果状态没有被注册过,注册状态
if (state == null || stateDic.ContainsKey(state.GetStateId()))
{
return;
}
stateDic.Add(state.GetStateId(), state);
}
//切换状态
public void SwitchState(uint stateId)
{
if (currentState != null && stateId == currentStateId)
{
return;
}
Istate newState;
Istate oldState;
stateDic.TryGetValue(stateId, out newState);
if (newState != null)
{
if(currentState!=null){
currentState.LeaveState(newState);
}
oldState = currentState;
currentState = newState;
currentState.EnterState(oldState);
}
}
public void Update()
{
if (currentState!=null)
{
currentState.OnUpdate();
}
}
public void FixedUpdate()
{
if (currentState != null)
{
currentState.OnFixedUpdate();
}
}
public void LateUpdate()
{
if (currentState != null)
{
currentState.OnLateUpdate();
}
}
}
UI管理类
using UnityEngine;
using System.Collections;
public enum EnemyStateId : uint
{
NullState = 0, PatrolState, ChaseState, AttackState
}
public class EnemyCtrl : AttackAndDamage
{
public Transform player;
public Transform[] paths;
private StateMachine fsm;
//初始化
void Awake()
{//初始化状态机
fsm = new StateMachine();
}
void Start()
{//注册状态
RegisterState();
}
private void RegisterState()
{//注册状态
fsm.RegisterState(new PatrolState(player, this.transform, paths));
fsm.RegisterState(new ChaseState(player, this.transform));
fsm.RegisterState(new AttackState(player, this.transform,baseAttack));
fsm.SwitchState((uint)EnemyStateId.PatrolState);
}
public void SwitchState(uint stateId) {
//进行状态切换
fsm.SwitchState(stateId);
}
// 更新数据
void Update()
{
//进行状态更新
fsm.Update();
}
void FixedUpdate()
{
fsm.FixedUpdate();
}
void LateUpdate()
{
fsm.LateUpdate();
}
}