介绍
有限状态机(Finite State Machine,简称FSM)是一种常用的行为建模工具,它是一种表示有限个状态和在这些状态之间的转移以及动作和条件的数学模型。在游戏中,有限状态机经常被用来表示游戏中的角色、NPC、AI 等行为逻辑。
组成
有限状态机通常由以下几部分组成:
-
状态(State):有限状态机中的状态表示游戏对象处于什么状态下,比如待机、行走、攻击等。
-
转移(Transition):状态之间的转移表示游戏对象在不同状态之间的切换,比如从待机状态切换到行走状态,从行走状态切换到攻击状态等。转移可能会依赖于一些条件,比如玩家按下攻击键、检测到敌人等。
-
动作(Action):动作是有限状态机中执行的一些操作,比如播放动画、发射子弹、播放声音等。动作可以与状态和转移关联起来,当有限状态机切换到某个状态或执行某个转移时,就会执行与之关联的动作。
用途
有限状态机在游戏中的应用非常广泛,比如:
-
游戏角色的行为逻辑:比如角色的移动、攻击、待机等行为。
-
AI 行为逻辑:比如敌人的巡逻、追击、攻击等行为。
-
游戏关卡逻辑:比如某个开关的状态、某个门的开关状态等。
-
UI 状态控制:比如某个按钮的状态、菜单的状态等。
实现
下面是一个简单的有限状态机的代码示例:
// 定义状态枚举
public enum State
{
Idle, // 空闲状态
Walk, // 行走状态
Run, // 跑步状态
Attack, // 攻击状态
Dead // 死亡状态
}
// 定义转移条件枚举
public enum Transition
{
IdleToWalk, // 从Idle状态转换到Walk状态的条件
WalkToRun, // 从Walk状态转换到Run状态的条件
RunToWalk, // 从Run状态转换到Walk状态的条件
WalkToIdle, // 从Walk状态转换到Idle状态的条件
AttackToIdle, // 从Attack状态转换到Idle状态的条件
AnyToDead // 任何状态都可以转换到Dead状态的条件
}
// 定义动作委托类型
public delegate void Action();
// 定义状态节点
public class StateNode
{
public State state; // 节点对应的状态
public Dictionary<Transition, State> transitions; // 转移字典,记录从当前状态可以转移到的其他状态
public Action enterAction; // 进入该状态时要执行的动作
public Action exitAction; // 退出该状态时要执行的动作
// 构造函数,初始化状态节点
public StateNode(State state)
{
this.state = state;
transitions = new Dictionary<Transition, State>();
}
// 添加转移条件
public void AddTransition(Transition transition, State toState)
{
transitions.Add(transition, toState);
}
}
// 定义有限状态机类
public class StateMachine
{
private StateNode currentStateNode; // 当前状态节点
private Dictionary<State, StateNode> stateNodes; // 状态字典,记录所有状态对应的节点
// 构造函数,初始化有限状态机
public StateMachine()
{
currentStateNode = null;
stateNodes = new Dictionary<State, StateNode>();
}
// 添加状态节点
public void AddState(StateNode node)
{
stateNodes.Add(node.state, node);
}
// 设置初始状态
public void SetInitialState(State state)
{
currentStateNode = stateNodes[state];
}
// 执行转移操作
public void PerformTransition(Transition transition)
{
// 如果当前状态节点存在该转移条件,则执行相应的动作,并将状态节点切换到目标状态节点
if (currentStateNode.transitions.ContainsKey(transition))
{
State toState = currentStateNode.transitions[transition];
currentStateNode.exitAction?.Invoke(); // 执行退出动作
currentStateNode = stateNodes[toState];
currentStateNode.enterAction?.Invoke(); // 执行进入动作
}
}
}
// 使用示例
public class PlayerController : MonoBehaviour
{
private StateMachine stateMachine;
private void Start()
{
// 创建状态节点
StateNode idleState = new StateNode(State.Idle);
StateNode walkState = new StateNode(State.Walk);
StateNode runState = new StateNode(State.Run);
StateNode attackState
StateNode deadState = new StateNode(State.Dead);
// 添加转移条件
idleState.AddTransition(Transition.IdleToWalk, State.Walk);
walkState.AddTransition(Transition.WalkToRun, State.Run);
runState.AddTransition(Transition.RunToWalk, State.Walk);
runState.AddTransition(Transition.AnyToDead, State.Dead);
walkState.AddTransition(Transition.WalkToIdle, State.Idle);
attackState.AddTransition(Transition.AttackToIdle, State.Idle);
deadState.AddTransition(Transition.AnyToDead, State.Dead);
// 设置状态节点的进入和退出动作
idleState.enterAction = () => { Debug.Log("Idle"); };
walkState.enterAction = () => { Debug.Log("Walk"); };
runState.enterAction = () => { Debug.Log("Run"); };
attackState.enterAction = () => { Debug.Log("Attack"); };
deadState.enterAction = () => { Debug.Log("Dead"); };
deadState.exitAction = () => { Debug.Log("Game Over"); };
// 创建有限状态机
stateMachine = new StateMachine();
stateMachine.AddState(idleState);
stateMachine.AddState(walkState);
stateMachine.AddState(runState);
stateMachine.AddState(attackState);
stateMachine.AddState(deadState);
stateMachine.SetInitialState(State.Idle);
// 执行转移操作
stateMachine.PerformTransition(Transition.IdleToWalk);
stateMachine.PerformTransition(Transition.WalkToRun);
stateMachine.PerformTransition(Transition.RunToWalk);
stateMachine.PerformTransition(Transition.WalkToIdle);
stateMachine.PerformTransition(Transition.IdleToWalk);
stateMachine.PerformTransition(Transition.AttackToIdle);
stateMachine.PerformTransition(Transition.AnyToDead);
}
}
上面的代码实现了一个简单的有限状态机,其中包含了几个状态和转移条件。每个状态节点都可以设置进入和退出时要执行的动作。在使用示例中,我们创建了一个有限状态机,并进行了一系列的状态转移操作。你可以根据自己的需要,修改状态节点的定义,添加更多的转移条件和动作,来实现更为复杂的状态机。