前言:在Unity中使用有限状态机(FSM)是管理游戏对象行为(如角色控制、AI逻辑、动画切换)的核心技术之一。在学习阶段,在没学习代码设计原则之前,我们可能把一个玩家所有的行为都在写一个脚本。这样是对于以后的维护来说是十分不利,以后有了新需求,因为都写在一起,代码黏糊糊在一起,牵一发而动全身。所以设计代码要规范,尽量要利于以后维护。
建议先看看下面这个,复习一下
代码设计原则
现在实现一个玩家简易FSM。
设计玩家目前只有二个状态,idle(待机)、move(移动),在代码设计上,根据单一原则,我们将这三个状态分别都设计为一个类,IdeleState,MoveState,二个类。对这个二个分析一下,无论是什么状态都会有进入,执行,退出三部分组成。对于状态公有的行为,提取出一个抽象类
- 定义状态抽象类
public abstract class State
{
// 当前状态进入时调用
public virtual void Enter()
{
Debug.Log("Enter State");
}
// 当前状态更新时调用
public virtual void Update()
{
Debug.Log("Update State");
}
// 当前状态退出时调用
public virtual void Exit()
{
Debug.Log("Exit State");
}
}
- 创建具体状态
定义具体的状态类,继承自 State,并实现具体的行为逻辑。
public class IdleState : State
{
public override void Enter()
{
Debug.Log("Enter Idle State");
}
public override void Update()
{
Debug.Log("Idle State Update");
}
public override void Exit()
{
Debug.Log("Exit Idle State");
}
}
public class MoveState : State
{
public override void Enter()
{
Debug.Log("Enter Move State");
}
public override void Update()
{
Debug.Log("Move State Update");
}
public override void Exit()
{
Debug.Log("Exit Move State");
}
}
- 有了状态后,我们需要一个专门的一个类,用来专门处理这些状态之间的切换,状态管理器负责管理当前状态,并处理状态切换。
public class StateHandler : MonoBehaviour
{
private State currentState;
// 切换到指定状态
public void ChangeState(State newState)
{
if (currentState != null)
{
currentState.Exit(); // 退出当前状态
}
currentState = newState;
currentState.Enter(); // 进入新状态
}
// 在 Update 中调用当前状态的 Update 方法
private void Update()
{
if (currentState != null)
{
currentState.Update();
}
}
}
- 在 Unity 中,将 StateHandler 脚本附加到一个 GameObject 上,并通过代码切换状态。同时新建一个PlayerController用来控制。
public class PlayerController : MonoBehaviour
{
private StateHandler stateHandler;
private IdleState idleState;
private MoveState moveState;
void Start()
{
stateHandler = GetComponent<StateHandler>();
idleState = new IdleState();
moveState = new MoveState();
// 初始状态为 Idle
stateHandler.ChangeState(idleState);
}
void Update()
{
// 示例:按下空格键切换到 Move 状态
if (Input.GetKeyDown(KeyCode.Space))
{
stateHandler.ChangeState(moveState);
}
// 示例:按下 ESC 键切换回 Idle 状态
if (Input.GetKeyDown(KeyCode.Escape))
{
stateHandler.ChangeState(idleState);
}
}
}
至此,一个简易的FSM完成了,看起来就二个状态,一下子写了四个脚本看起来很麻烦,感觉有点小题大做的。那么我们有了新的需求给玩家新加一个状态,比如攻击。这个时候我们只需要新增东西,而不需要修改原本有的东西,这就符合开闭原则。
第一步,新建一个AttackState类
public class AttackState : State
{
public override void Enter()
{
Debug.Log("Enter Move State");
}
public override void Update()
{
Debug.Log("Move State Update");
}
public override void Exit()
{
Debug.Log("Exit Move State");
}
}
第二步,在PlayerContoller里,添加。
public class PlayerController : MonoBehaviour
{
private StateHandler stateHandler;
private IdleState idleState;
private MoveState moveState;
//新增攻击状态
private AttackState attackState;
void Start()
{
stateHandler = GetComponent<StateHandler>();
idleState = new IdleState();
moveState = new MoveState();
//New 新增攻击状态
attackState = new AttackState();
// 初始状态为 Idle
stateHandler.ChangeState(idleState);
}
void Update()
{
// 示例:按下空格键切换到 Move 状态
if (Input.GetKeyDown(KeyCode.Space))
{
stateHandler.ChangeState(moveState);
}
// 示例:按下 ESC 键切换回 Idle 状态
if (Input.GetKeyDown(KeyCode.Escape))
{
stateHandler.ChangeState(idleState);
}
// 示例:按下鼠标左键切换回 Attack 状态
if (Input.GetKeyDown(KeyCode.Mouse0))
{
stateHandler.ChangeState(idleState);
}
}
}
看,很简单只要新增东西,不需要修改原本有的东西,而且状态都分开了,不相互粘连,便于维护,就算以后有了新需求三百六十五度阿姆斯特朗回旋喷气式移动,也只需要MoveState状态里修改,看起来很干净,也减少bug出现的可能性。