[开发者心得] 有限状态机

[复制链接]
1033 |3
犯困嫌疑人 发表于 2023-6-19 11:47:42 | 显示全部楼层 |阅读模式
本帖最后由 犯困嫌疑人 于 2023-6-19 11:51 编辑

概述有限状态机是一种用来进行对象行为建模的工具,作用是描述对象在它的生命周期内所经历的状态序列,以及如何响应来自外界的各种事件。
介绍意图:将业务流程状态化,划分状态和相应的触发事件与动作,利用生命周期事件进行控制与执行。
主要解决:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。
何时使用:代码中存在大量的if else来判断对象的状态时;一个对象的状态越多、发生的事件越多,就越适合采用有限状态机的写法
如何解决:将各种具体的状态抽象出来。
优点:有限状态机的写法,逻辑清晰,表达力强,有利于封装事件,实现后易于阅读和理解。
缺点:系统类和对象的个数增加;结构和实现都较为繁杂,使用不当会导致程序结构和代码混乱;对于新增的状态,往往需要修改负责状态转换的代码对"开闭原则"的支持不友好。
使用场景:1.行为随状态改变而改变的场景下;2.行为受到状态约束的时候;
特点:状态总数是有限的;任一时刻,只处在一种状态之中;某种条件下会转换状态。
简单实现:


/**
* 状态机
*/
class StateMachine {
    /**当前状态 */
    private _currentState: StateBase;
    /**所有的状态 */
    private _allStates: StateBase[] = [];
    /**状态对象 */
    private _actor: Transform;
    constructor(actor: Transform) {
        this._actor = actor;
        //准备好所有的状态实例
        this._allStates = [new BornState(this, actor), new MoveForwardState(this, actor), new RollForwardState(this, actor)]
        this._currentState = this._allStates[0];
        this._currentState.onEnter();
    }
    /**切换到另一个状态 */
    public toState(name: string) {
        if (this._currentState.constructor.name == name) return;
        let nextState: StateBase = this._allStates.find(i => i.constructor.name == name);
        this._currentState.onExit();
        this._currentState = nextState;
        this._currentState.onEnter();
    }
    /**帧驱动,也算是对外接受条件的一个接口 */
    public update(dt: number) {
        this._currentState.onUpdate(dt);
    }
}
/**
* 状态基类
*/
class StateBase {
    //归属于哪个状态机
    protected _fsm: StateMachine;
    protected _actor: Transform;
    constructor(fsm: StateMachine, actor: Transform) {
        this._fsm = fsm;
        this._actor = actor;
    }
    /**进入状态 */
    onEnter() { }
    /**帧驱动 */
    onUpdate(dt: number) { }
    /**退出状态 */
    onExit() { }
}
/**
* 向前行走
*/
class MoveForwardState extends StateBase {
    private readonly speed: number = 1;
    override onUpdate(dt: number): void {
        this._actor.location.x += dt * this.speed;
    }
}
/**
* 向前滚
*/
class RollForwardState extends StateBase {
    private readonly moveSpeed: number = 1;
    private readonly rollSpeed: number = 1;
    override onUpdate(dt: number) {
        this._actor.location.x += dt * this.moveSpeed;
        this._actor.rotation.y += dt * this.rollSpeed;
    }
}
/**
* 出生状态
*/
class BornState extends StateBase {
    private readonly bornLocation: Vector = Vector.zero;
    override onEnter(): void {
        this._actor.location = this.bornLocation;
    }
}

let fsm = new StateMachine(new Transform());
存在的问题:每次在实现状态机的时候我们都要去写一遍有限状态实例,当前状态,状态切换。
改造成泛型:



/**
* 泛型实现
*/
class StateMachine<A> {
    /**当前状态 */
    private _currentState: StateBase<A>;
    /**所有的状态 */
    private _allStates: StateBase<A>[] = [];
    /**表现对象 */
    private _actor: A;
    constructor(actor: A, ...states: { new(fsm: StateMachine<A>, actor: A): StateBase<A> }[]) {
        this._actor = actor;
        //准备好所有的状态实例
        for (let i of states) {
            this._allStates.push(new i(this, this._actor));
        }
        this._currentState = this._allStates[0];
        this._currentState.onEnter();
    }
   /**切换到另一个状态 */
    public toState(state: new (target, machine) => StateBase<A>) {
        if (this._currentState.constructor.name == state.name) return;
        let nextState: StateBase<A> = this._allStates.find(i => i.constructor.name == state.name);
        this._currentState.onExit();
        this._currentState = nextState;
        this._currentState.onEnter();
    }
    /**帧驱动,也算是对外接受条件的一个接口 */
    public update(dt: number) {
        this._currentState.onUpdate(dt);
    }
}
/**
* 状态基类
*/
class StateBase<A> {
    //归属于哪个状态机
    protected _fsm: StateMachine<A>;
    protected _actor: A;
    constructor(fsm: StateMachine<A>, actor: A) {
        this._fsm = fsm;
        this._actor = actor;
    }
    /**进入状态 */
    onEnter() { }
    /**帧驱动 */
    onUpdate(dt: number) { }
    /**退出状态 */
    onExit() { }
}

使用:


/**
* 向前行走
*/
class MoveForwardState extends StateBase<Transform> {
    private readonly speed: number = 1;
    override onUpdate(dt: number): void {
        this._actor.location.x += dt * this.speed;
    }
}
/**
* 向前滚
*/
class RollForwardState extends StateBase<Transform> {
    private readonly moveSpeed: number = 1;
    private readonly rollSpeed: number = 1;
    override onUpdate(dt: number) {
        this._actor.location.x += dt * this.moveSpeed;
        this._actor.rotation.y += dt * this.rollSpeed;
    }
}
/**
* 出生状态
*/
class BornState extends StateBase<Transform> {
    private readonly bornLocation: Vector = Vector.zero;
    override onEnter(): void {
        this._actor.location = this.bornLocation;
    }
}
let fsm = new StateMachine<Transform>(new Transform(), BornState, MoveForwardState, RollForwardState);













回复

使用道具 举报

吃到苦就是得到了甜 发表于 2023-6-19 15:01:38 | 显示全部楼层
学习
回复

使用道具 举报

猪头BOOM 发表于 2023-7-2 18:12:16 | 显示全部楼层
有没有大佬可以解释下this._currentState.constructor.name是什么意思/原理?类名吗?
回复

使用道具 举报

木叶寂琉璃 发表于 2023-12-26 02:39:33 | 显示全部楼层
本帖最后由 木叶寂琉璃 于 2023-12-26 02:41 编辑
猪头BOOM 发表于 2023-7-2 18:12
有没有大佬可以解释下this._currentState.constructor.name是什么意思/原理?类名吗? ...
构造函数的名称,和类名相同。相当于判断切换的状态是不是当前状态
回复

使用道具 举报

热门版块
快速回复 返回顶部 返回列表