初学者,自用笔记
Switch简单模式
public enum StateType
{
Idle,Find_Enemy,Attack,Die,Success,
}
void Start()
{
curState = StateType.Idle;
}
switch (curState)
{
case StateType.Idle:OnIdle();
break;
case StateType.Attack:OnAttack();
break;
case StateType.Die:OnDie();
break;
case StateType.Find_Enemy:OnFind_Enemy();
break;
default:OnSuccess();
break;
}
void On...
FSM有限状态机
by B站 打工人小棋
namespace TheFSM
{
public enum StateType
{
Idle, Find_Enemy, Attack, Die, Success,
}
public interface IState
{
void OnEnter();//状态机进入当前状态时被调用
void OnExit();//状态机离开当前状态时被调用
void OnUpdate();//每帧更新时被调用
void OnCheck();//检查状态转移的条件是否满足
void OnFixUpdate();
}
[Serializable]
public class Blackboard//黑板设计模式
{
//存储共享的,向外展示的,可配置的数据
}
public class FSM
{
public IState curState;
public Dictionary<StateType, IState> states;
public Blackboard blackboard;
public FSM(Blackboard blackboard)
{
this.states= new Dictionary<StateType, IState>();
this.blackboard = blackboard;
}
public void AddState(StateType stateType,IState state)//初始化
{
if (states.ContainsKey(stateType))
{
return;
}
states.Add(stateType, state);
}
public void SwithState(StateType stateType)//切换状态
{
if (!states.ContainsKey(stateType)) { return; }
if (curState != null)
{
curState.OnExit();
}
curState=states[stateType];
curState.OnEnter();
}
public void OnUpdate()
{
curState.OnUpdate();
}
public StateType GetNowType()
{
return nowType;
}
}
}
用例
using TheFSM
//父状态
public class PlayerState : IState
{
public FSM fsm;
public Player player;
public string StateName;
protected float stateTimer;
protected bool triggerCallecd=false;
public PlayerState(FSM fsm, Player player, string stateName)
{
this.fsm = fsm;
this.player = player;
StateName = stateName;
}
public virtual void OnCheck()
{
throw new System.NotImplementedException();
}
public virtual void OnEnter()
{
player.animator.SetBool(StateName,true);
triggerCallecd = false;
}
public virtual void OnExit()
{
player.animator.SetBool(StateName,false);
}
public virtual void OnFixUpdate()
{
throw new System.NotImplementedException();
}
public virtual void OnUpdate()
{
ad = Input.GetAxisRaw("Horizontal");
stateTimer -= Time.deltaTime;
}
public virtual void AnimationFinishTrigger()
{
triggerCallecd=true;
}
public void SetMoveVelocity()
{
player.SetVelocity(ad * player.moveSpeed, player.rb.linearVelocityY);
}
}
//空中判断状态
public class PlayerAirState : PlayerState
{
public PlayerAirState(FSM fsm, Player player, string stateName) : base(fsm, player, stateName)
{
}
public override void OnCheck()
{
base.OnCheck();
}
public override void OnEnter()
{
base.OnEnter();
}
public override void OnExit()
{
base.OnExit();
player.SetVelocity(ad * player.moveSpeed, player.rb.linearVelocityY);
}
public override void OnFixUpdate()
{
base.OnFixUpdate();
}
public override void OnUpdate()
{
base.OnUpdate();
if (player.isWallDetected()) fsm.SwithState(StateType.WallSlide);
if (player.isGroundDetected()) { fsm.SwithState(StateType.Idle); }
player.SetVelocity(ad * player.moveSpeed, player.rb.linearVelocityY);
}
}
//地面判断状态
public class PlayerGroundedState : PlayerState
{
public PlayerGroundedState(FSM fsm, Player player, string stateName) : base(fsm, player, stateName)
{
}
public override void OnCheck()
{
base.OnCheck();
}
public override void OnEnter()
{
base.OnEnter();
}
public override void OnExit()
{
base.OnExit();
}
public override void OnFixUpdate()
{
base.OnFixUpdate();
}
public override void OnUpdate()
{
base.OnUpdate();
if(Input.GetKeyDown(KeyCode.Mouse0))
{
fsm.SwithState(StateType.PrimaryAttack);
}
if (!player.isGroundDetected()) fsm.SwithState(StateType.Air);
if(Input.GetKeyDown(KeyCode.Space)&&player.isGroundDetected())
{
fsm.SwithState(StateType.Jump);
}
}
//待机状态
public class PlayerIdleState : PlayerGroundedState
{
public PlayerIdleState(FSM fsm, Player player, string stateName) : base(fsm, player, stateName)
{
}
public override void OnExit()
{
base.OnExit();
}
public override void OnEnter()
{
base.OnEnter();
player.SetVelocity(0, player.rb.linearVelocityY);
}
public override void OnUpdate()
{
base.OnUpdate();
if (player.isWallDetected() && ad == player.now方向) return;
if (ad != 0&&) fsm.SwithState(StateType.Move);
}
}
}
HSM层级状态机
HState
public abstract class HState
{
// 父状态引用
public HState Parent { get; private set; }
// 当前激活的子状态
public HState ActiveSubState { get; private set; }
// 初始子状态(父状态切换后会自动进入该子状态)
public HState InitialSubState { get; protected set; }
// 缓存当前状态的根状态
public HState Root { get; private set; }
// 所属的状态机
protected HSM Machine { get; private set; }
protected HState(HSM machine, HState parent = null)
{
Machine = machine;
SetParent(parent);
}
public void SetParent(HState parent)
{
Parent = parent;
GetRoot();
}
// 计算并更新根状态缓存
private void GetRoot()
{
// 如果没有父状态,自己就是根状态
if (Parent == null)
{
Root = this;
}
else
{
// 直接复用父状态的根状态
Root = Parent.Root;
}
}
// 设置当前激活的子状态
public void SetSubState(HState sub)
{
ActiveSubState = sub;
}
// 状态进入逻辑(叶子/父状态都可覆盖)
public virtual void Enter() { }
// 状态退出逻辑(叶子/父状态都可覆盖)
public virtual void Exit() { }
// 帧更新逻辑:父状态先执行自身逻辑,再执行激活的子状态
public virtual void Update()
{
OnUpdate();
ActiveSubState?.Update();
}
// 固定帧更新逻辑:同Update的层级执行逻辑
public virtual void FixedUpdate()
{
OnFixedUpdate();
ActiveSubState?.FixedUpdate();
}
// 状态检查逻辑:同Update的层级执行逻辑
public virtual void OnCheck()
{
OnCheckSelf();
ActiveSubState?.OnCheck();
}
// 子类重写的自身更新逻辑
// 业务逻辑写在OnUadte中,Update负责固定的层级执行流程
protected virtual void OnUpdate() { }
// 子类重写的自身固定帧更新逻辑
protected virtual void OnFixedUpdate() { }
// 子类重写的自身状态检查逻辑
protected virtual void OnCheckSelf() { }
// 向上遍历获取当前状态到根状态的路径(迭代器)
public IEnumerable<HState> GetPathToRoot()
{
for (HState s = this; s != null; s = s.Parent)
yield return s;
}
}
hsm
public class HSM
{
// 当前最底层的叶子状态(具体执行逻辑的状态)
public HState CurrentLeaf { get; private set; }
// 当前最顶层的根状态(状态机的入口)
public HState CurrentRoot { get; private set; }
// 状态类型与状态实例的映射表(支持通过枚举快速查找状态)
private readonly Dictionary<StateType, HState> _states = new();
// 注册状态到状态机
public void AddState(StateType type, HState state)
{
_states.TryAdd(type, state);
}
// 重载:通过状态类型切换状态
public void SwitchState(StateType type)
{
if (_states.TryGetValue(type, out var target))
SwitchState(target);
}
// 核心:切换到指定状态实例
public void SwitchState(HState target)
{
// 空值判断 + 重复状态判断(避免无意义的重复切换)
if (target == null || CurrentLeaf == target) return;
// 1 找到当前叶子状态和目标状态的最近公共祖先(LCA)
var from = CurrentLeaf;
var lca = FindLCA(from, target);
// 2 退出:从当前叶子向上退出到LCA的子级(不退出LCA)
ExitUpTo(from, lca);
// 3 进入:从LCA向下进入到目标状态(按父→子顺序执行Enter)
EnterDownTo(target, lca);
// 4 如果目标状态是父状态,自动钻到最深的初始子状态(保证CurrentLeaf是叶子)
var leaf = DrillToInitialLeaf(target);
CurrentLeaf = leaf;
CurrentRoot = leaf.Root;
}
// 驱动根状态的帧更新
public void Update() => CurrentRoot?.Update();
// 驱动根状态的固定帧更新
public void FixedUpdate() => CurrentRoot?.FixedUpdate();
// 驱动根状态的状态检查
public void OnCheck() => CurrentRoot?.OnCheck();
// 查找两个状态的最近公共祖先(LCA)
private static HState FindLCA(HState a, HState b)
{
if (a == null || b == null) return null;
// 先把a的路径存入集合
var set = new HashSet<HState>(a.GetPathToRoot());
// 遍历b的路径,第一个在集合中的状态就是LCA
foreach (var s in b.GetPathToRoot())
if (set.Contains(s)) return s;
return null;
}
// 从当前叶子状态向上退出到LCA(不包含LCA)
private static void ExitUpTo(HState fromLeaf, HState lca)
{
for (var s = fromLeaf; s != null && s != lca; s = s.Parent)
s.Exit();
}
// 从LCA向下进入到目标状态(按父→子顺序执行Enter)
private static void EnterDownTo(HState target, HState lca)
{
// 收集目标状态到LCA的路径(不包含LCA),存入栈中反转顺序
var stack = new Stack<HState>();
for (var s = target; s != null && s != lca; s = s.Parent)
stack.Push(s);
// 从栈中弹出状态(父→子顺序),执行Enter并设置父状态的激活子状态
while (stack.Count > 0)
{
var s = stack.Pop();
s.Enter();
// 让父状态记录当前激活的子状态
s.Parent?.SetSubState(s);
}
}
// 从指定状态向下钻取到最深的初始子状态
private static HState DrillToInitialLeaf(HState state)
{
var s = state;
// 循环进入初始子状态,直到没有初始子状态为止
while (s.InitialSubState != null)
{
var next = s.InitialSubState;
next.Enter();
s.SetSubState(next);
s = next;
}
return s;
}
}

京公网安备 11010502036488号