50行写个简单的状态机

何为简答?我看得懂就是简单
何为状态机? 状态机是有限状态自动机的简称。
好了不废话了,上代码

/** 状态 */
export class FSMState {
    public name: string = null;
    public translationMap: Map<string, FSMTranslation> = null;  //<指令名,跳转>
    constructor(name: string) {
        this.name = name;
        this.translationMap = new Map<string, FSMTranslation>();
    }
}
/** 跳转 */
export class FSMTranslation {
    public name: string = null; //指令名
    public fromState: FSMState = null;  //上一个状态
    public toState: FSMState = null;    //下一个状态
    public callFunction: Function = null;   //状态对应的回调,如果有需要可以自己添加 EnterCallback,ExitCallback,然后在handleEvent里面调用,这里我就只写一个回调了
    public constructor(name: string, fromState: FSMState, toState: FSMState, callFunction: Function) {
        this.name = name;
        this.fromState = fromState;
        this.toState = toState;
        this.callFunction = callFunction;
    }
}
/** 状态机 */
export class FSM {
    private _currState: FSMState = null; // 当前状态
    public get currState(): FSMState { return this._currState; }

    public stateMap: Map<string, FSMState> = new Map<string, FSMState>();

    public addState(state: FSMState): void {
        this.stateMap.set(state.name, state);
    }

    public addTranslation(translation: FSMTranslation): void {
        this.stateMap.get(translation.fromState.name).translationMap.set(translation.name, translation);
    }

    /** 启动状态机 */
    public start(state: FSMState): void {
        this._currState = state;
    }

    public handleEvent(name: string): void {
        if (this._currState != null && this._currState.translationMap.has(name)) {
            this._currState.translationMap.get(name).callFunction();
            this._currState = this._currState.translationMap.get(name).toState;
        }
    }
}

好了,写完了。使用如下:

import { FSM, FSMState, FSMTranslation } from "../Core/FSM";

const { ccclass, property } = cc._decorator;

enum FSMStateType {
    Idle = "Idle",
    Jump = "Jump",
    Die = "Die",
}

enum FSMTranslationType {
    Touch_Jump = "Touch_Jump",
    On_Idle = "On_Idle",
    On_Idle_Die = "On_Idle_Die",
    On_Jump_Die = "On_Jump_Die",
}

@ccclass
export default class Test extends cc.Component {

    private _fsm: FSM = new FSM();

    start() {
        // 创建状态
        let idleState: FSMState = new FSMState(FSMStateType.Idle);
        let jumpState: FSMState = new FSMState(FSMStateType.Jump);
        let dieState: FSMState = new FSMState(FSMStateType.Die);

        // 创建跳转
        let idleToJumpTranslation: FSMTranslation = new FSMTranslation(FSMTranslationType.Touch_Jump, idleState, jumpState, this.jump.bind(this));
        let jumpToIdleTranslation: FSMTranslation = new FSMTranslation(FSMTranslationType.On_Idle, jumpState, idleState, this.idle.bind(this));
        let idleToDieTranslation: FSMTranslation = new FSMTranslation(FSMTranslationType.On_Idle_Die, idleState, dieState, this.idleDie.bind(this));
        let jumpToDieTranslation: FSMTranslation = new FSMTranslation(FSMTranslationType.On_Jump_Die, jumpState, dieState, this.jumpDie.bind(this));

        // 添加状态
        this._fsm.addState(idleState);
        this._fsm.addState(jumpState);
        this._fsm.addState(dieState);

        // 添加跳转
        this._fsm.addTranslation(idleToJumpTranslation);
        this._fsm.addTranslation(jumpToIdleTranslation);
        this._fsm.addTranslation(idleToDieTranslation);
        this._fsm.addTranslation(jumpToDieTranslation);

        this._fsm.start(idleState);

    }


    // ---------触发事件切换状态------------------------------------------------

    /** 起跳 */
    private onJumpBtn(): void {
        if (this._fsm.currState.name != FSMStateType.Idle) return;

        this._fsm.handleEvent(FSMTranslationType.Touch_Jump);
        this.unscheduleAllCallbacks();
        this.scheduleOnce(() => { this._fsm.handleEvent(FSMTranslationType.On_Idle); }, 0.5);
    }

    /** 嗝儿屁 */
    private onDieBtn(): void {
        if (this._fsm.currState.name == FSMStateType.Jump) {
            this._fsm.handleEvent(FSMTranslationType.On_Jump_Die);
        } else if (this._fsm.currState.name == FSMStateType.Idle) {
            this._fsm.handleEvent(FSMTranslationType.On_Idle_Die);
        }
    }

    // ---------------回调---------------------------------------------

    private jump(): void { console.log("jumping"); }

    private idle(): void { console.log("idle"); }

    private jumpDie(): void { console.log("jumpDie"); }

    private idleDie(): void { console.log("idleDie"); }
}
5赞

这个状态机的状态跳转限制,在哪里?

:grinning:

简单状态机,限制全靠自律 :rofl:
handleEvent 这个就是他的限制

九个字插个简单的眼。

看到了,在使用区的代码里看到了,一时没反应过来,因为状态机封装复用率很低,我一般直接在状态机内部写转换表

//FSM 状态机
type FSM struct {
	currState  state
	transition []trans
	mutx       *sync.RWMutex
}

//NewFSM .
func NewFSM() FSM {
	return FSM{
		StateIniting,
		[]trans{
			{InputLogin, StatePlaying, StateIniting},
			{InputLogin, StateOffline, StateIniting},
			{InputFinish, StateIniting, StatePlaying},
			{InputDisconnect, StatePlaying, StateOffline},
			{InputDisconnect, StateOffline, StateOffline},
			{InputFail, StateIniting, StateOffline},
		},
		new(sync.RWMutex),
	}
}
1赞

太简单的状态机,限制全靠使用者自律(即在状态机外部做条件判断或不判断),可以做为参考,但是不建议在项目中使用。
建议补全 handleEvent 方法,在此方法内做状态判断和跳转判断,以及必要的调试日志输出,以快速定位问题

1赞

我应该改个标题:50行写个简陋的状态机。
又不是不能用.jpg。哈哈