Unity有限制状态机FSM
我是标题
- 前言
- 有限制状态机框架
- 框架图:
- 主要代码:
前言
一般的小型游戏的状态机会使用一个枚举类来枚举所有的状态,然后使用一个switch case来处理所有状态的行为逻辑,但是用这种方式会形成大量的冗余,因为所有的行为逻辑都在一个脚本中,所以团队开发来说模块性不足,可扩展性低。所以就有了有限制状态机(FSM),行为逻辑写在别的脚本中,每个行为逻辑之间互相独立,并且通过一个状态控制中心来进行控制,同时有“大脑”BlackBoard来实现整体之间的数据互通和共享,可拓展性和模块性强,耦合度低。
有限制状态机框架
来源:B站打工人小棋 跳转
框架图:
主要代码:
黑板类:提供全局的数据共享
public class BlackBoard
{
private Dictionary<string, object> data = new Dictionary<string, object>();
// 获取数据
public T GetData<T>(string key)
{
if (data.TryGetValue(key, out object value))
{
return (T)value;
}
return default(T);
}
// 设置数据
public void SetData(string key, object value)
{
if (data.ContainsKey(key))
{
data[key] = value;
}
else
{
data.Add(key, value);
}
}
}
定义一个接口类作为所有动作的基类
public interface Istate
{
void OnEnter();
void OnExit();
void OnUpdate();
}
状态机类:
容纳键值对查找的字典。
对外公开的Add和Remove和 SwitchState的方法。
全局共享的黑板。
目前的状态。
public class FSM
{
public enum State
{
Idle,
Move,
Attack,
}
public Istate currentState;
public Dictionary<State, Istate> states;
public BlackBoard blackBoard;
public FSM()
{
blackBoard = new BlackBoard();
states = new Dictionary<State, Istate>();
}
public void AddState(State stateType, Istate state)
{
if(states.ContainsKey(stateType))
{
Debug.LogWarning("ISEXIST");
return;
}
states.Add(stateType, state);
}
public void RemoveState(State stateType)
{
if( states.ContainsKey(stateType))
{
states.Remove(stateType);
}
else
{
Debug.LogWarning("DO NOT EXIST");
}
}
public void SwitchState(State stateType)
{
if(!states.ContainsKey(stateType))
{
Debug.LogWarning("DO NOT EXIST");
return;
}
if(currentState != null)
{
currentState.OnExit();
}
if(states.TryGetValue(stateType, out Istate theNewState)) currentState = theNewState;
currentState.OnEnter();
}
public void OnUpdate()
{
currentState?.OnUpdate();
}
}
Idle状态定义示例:
需要接受有玩家脚本的一个构造函数,否者无法调用到玩家脚本中的公开的数据。
public class MoveState : Istate
{
private PlayerController playerController;
public MoveState(PlayerController playerController)
{
this.playerController = playerController;
}
public void OnEnter()
{
Debug.Log("Enter Move State");
}
public void OnExit()
{
Debug.Log("Exit Move State");
}
public void OnUpdate()
{
// 移动逻辑
float horizontalInput = Input.GetAxis("Horizontal");
float verticalInput = Input.GetAxis("Vertical");
Vector3 movement = new Vector3(horizontalInput, 0, verticalInput) * playerController.moveSpeed * Time.deltaTime;
playerController.transform.Translate(movement);
// 检测是否停止移动
if (horizontalInput == 0 && verticalInput == 0)
{
playerController.fsm.SwitchState(FSM.State.Idle);
}
// 检测是否有攻击输入
if (Input.GetKeyDown(KeyCode.Space))
{
//更换到攻击模式 如果按下空格的话
playerController.fsm.SwitchState(FSM.State.Attack);
}
}
}
使用:
public class PlayerController : MonoBehaviour
{
public float moveSpeed = 5f;
public FSM fsm;
void Start()
{
fsm = new FSM();
// 添加状态
fsm.AddState(FSM.State.Idle, new IdleState(this));
fsm.AddState(FSM.State.Move, new MoveState(this));
fsm.AddState(FSM.State.Attack, new AttackState(this));
// 初始状态为空闲
fsm.SwitchState(FSM.State.Idle);
}
void Update()
{
fsm.OnUpdate();
}
}