初学者,自用笔记

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;
    }
}