cocos creator从零开发简单框架(完结)-状态机

新建framework/scripts/state/State.ts,状态基类,内容如下 。

export default class State {
    public static readonly invalidState = 'invalid'

    protected _owner: any
    public get owner(): any { return this._owner }


    public constructor(owner: any) {
        this._owner = owner
    }


    // 进入状态
    public onEnter(_arg?: any, _lastKey?: string) { }

    // 重新进入状态
    public onReEnter(_arg?: any) { }

    // 每帧调用
    public onUpdate(_dt: number) { }

    // 退出状态
    public onExit(_nextKey: string) { }

    // 获取状态ID
    public getStateKey(): string {
        return State.invalidState
    }
}

新建framework/scripts/state/StateMachine.ts,状态管理器,内容如下 。

import State from "./State"


export default class StateMachine {
    private _owner: any
    private _stateMap: Map<string, State> = new Map()
    private _currentState: State


    public constructor(owner: any) {
        this._owner = owner
    }


    public register(state: State) {
        if (this._owner != state.owner) {
            console.error('StateMachine.register owner 不一致')
            return
        }

        this._stateMap.set(state.getStateKey(), state)
    }

    public enter(key: string, _arg?: any) {
        const state = this._stateMap.get(key)
        if (!state) {
            console.error('StateMachine.enter 不存在 state:' + key)
            return
        }

        if (!this._currentState) {
            state.onEnter(_arg)

            this._currentState = state
        } else {
            if (this._currentState.getStateKey() == key) {
                state.onReEnter(_arg)
            } else {
                this._currentState.onExit(state.getStateKey())

                state.onEnter(_arg, this._currentState.getStateKey())

                this._currentState = state
            }
        }
    }

    public update(dt: number) {
        if (this._currentState) {
            this._currentState.onUpdate(dt)
        }
    }

    public getCurrentStateKey(): string {
        if (this._currentState) {
            return this._currentState.getStateKey()
        }

        return State.invalidState
    }

    public clear() {
        if (this._currentState) {
            this._currentState.onExit(State.invalidState)
        }

        this._owner = null
        this._stateMap.clear()
        this._stateMap = null
        this._currentState = null
    }
}

状态机的核心代码就这两个类,下面写状态机的示例,控制一个方块左右和上下运动。总共两个状态,一个状态控制方块左右运动,一个状态控制方块上下运动。

新建scripts/state/StateDef.ts,状态 key 定义,内容如下 。

export default class StateDef {
    public static readonly leftRight = 'leftRight'
    public static readonly upDown = 'upDown'
}

新建scripts/state/StateLeftRight.ts,左右运动状态,内容如下 。

import State from "../../framework/scripts/state/State"
import PanelStateMachine from "../../scripts/PanelStateMachine"
import StateDef from "./StateDef"


export default class StateLeftRight extends State {
    private _player: cc.Node

    private _speed: number = 400
    private _moveLeft: boolean = false
    private _moveRight: boolean = false


    public onEnter(_arg?: any, _lastKey?: string): void {
        cc.log('StateLeftRight 进入状态 _lastKey:', _lastKey)
        this._player = (this._owner as PanelStateMachine).skin.getChildByName('player')

        cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this)
        cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this)
    }

    public onReEnter(_arg?: any): void {
        cc.log('StateLeftRight 重新进入状态')
    }

    public onUpdate(dt: number): void {
        if (this._moveLeft) {
            this._player.x -= this._speed * dt
            const minX = -cc.winSize.width / 2 + this._player.width / 2
            if (this._player.x < minX) {
                this._player.x = minX
            }
        } else if (this._moveRight) {
            this._player.x += this._speed * dt
            const maxX = cc.winSize.width / 2 - this._player.width / 2
            if (this._player.x > maxX) {
                this._player.x = maxX
            }
        }
    }

    public onExit(_nextKey: string): void {
        cc.log('StateLeftRight 退出状态 _nextKey:', _nextKey)
        // cc.systemEvent.off(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this)
        // cc.systemEvent.off(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this)
    }

    public getStateKey(): string {
        return StateDef.leftRight
    }


    private onKeyDown(et: cc.Event.EventKeyboard) {
        if (et.keyCode == cc.macro.KEY.a) {
            this._moveLeft = true
        } else if (et.keyCode == cc.macro.KEY.d) {
            this._moveRight = true
        }
    }

    private onKeyUp(et: cc.Event.EventKeyboard) {
        if (et.keyCode == cc.macro.KEY.a) {
            this._moveLeft = false
        } else if (et.keyCode == cc.macro.KEY.d) {
            this._moveRight = false
        }
    }
}

新建scripts/state/StateUpDown.ts,上下运动状态,内容如下 。

import State from "../../framework/scripts/state/State"
import PanelStateMachine from "../../scripts/PanelStateMachine"
import StateDef from "./StateDef"


export default class StateUpDown extends State {
    private _player: cc.Node

    private _speed: number = 400
    private _moveUp: boolean = false
    private _moveDown: boolean = false


    public onEnter(_arg?: any, _lastKey?: string): void {
        cc.log('StateUpDown 进入状态 _lastKey:', _lastKey)
        this._player = (this._owner as PanelStateMachine).skin.getChildByName('player')

        cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this)
        cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this)
    }

    public onReEnter(_arg?: any): void {
        cc.log('StateUpDown 重新进入状态')
    }

    public onUpdate(dt: number): void {
        if (this._moveUp) {
            this._player.y += this._speed * dt
            const maxY = cc.winSize.height / 2 - this._player.height / 2
            if (this._player.y > maxY) {
                this._player.y = maxY
            }
        } else if (this._moveDown) {
            this._player.y -= this._speed * dt
            const minY = -cc.winSize.height / 2 + this._player.height / 2
            if (this._player.y < minY) {
                this._player.y = minY
            }
        }
    }

    public onExit(_nextKey: string): void {
        cc.log('StateUpDown 退出状态 _nextKey:', _nextKey)
        // cc.systemEvent.off(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this)
        // cc.systemEvent.off(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this)
    }

    public getStateKey(): string {
        return StateDef.upDown
    }


    private onKeyDown(et: cc.Event.EventKeyboard) {
        if (et.keyCode == cc.macro.KEY.w) {
            this._moveUp = true
        } else if (et.keyCode == cc.macro.KEY.s) {
            this._moveDown = true
        }
    }

    private onKeyUp(et: cc.Event.EventKeyboard) {
        if (et.keyCode == cc.macro.KEY.w) {
            this._moveUp = false
        } else if (et.keyCode == cc.macro.KEY.s) {
            this._moveDown = false
        }
    }
}

场景创建空节点并重命名为PanelStateMachine,在其下创建Sprite (单色)组件并重命名为bg,大小设置为750x1334即设计分辨率大小,颜色修改为淡黄色#747155。在顶部创建一个名为BtnLR文本为左右运动的按钮,再创建一个名为BtnUD文本为上下运动的按钮。在底部创建一个名为BtnClose文本为关闭的按钮用来关闭面板。创建Sprite (单色)组件并重命名为player。把 PanelStateMachine节点拖动到resources目录下并从场景删除。

新建scripts/PanelStateMachine.ts,内容如下。

import AppConstants from "../framework/scripts/AppConstants"
import TimerMgr from "../framework/scripts/manager/TimerMgr"
import StateMachine from "../framework/scripts/state/StateMachine"
import PanelBase from "../framework/scripts/view/PanelBase"
import StateDef from "./state/StateDef"
import StateLeftRight from "./state/StateLeftRight"
import StateUpDown from "./state/StateUpDown"


export default class PanelStateMachine extends PanelBase {
    public skinPath: string = 'PanelStateMachine'

    public panelMaskStyle: number = AppConstants.panelMaskStyle.Close | AppConstants.panelMaskStyle.Black //关闭组件(点击面板区域外会关闭面板)加半透明组件
    public panelShowStyle: number = AppConstants.panelShowStyle.Normal

    private _machine: StateMachine


    protected onInit(): void {
        this._machine = new StateMachine(this)

        this._machine.register(new StateLeftRight(this))
        this._machine.register(new StateUpDown(this))
    }

    protected onInitDone(): void {
        this._machine.enter(StateDef.leftRight)

        TimerMgr.inst.add(0, this.update, this, 0)
    }

    protected onButtonClick(button: cc.Node) {
        if (button.name == 'BtnClose') {
            this.close()
        } else if (button.name == 'BtnLR') {
            this._machine.enter(StateDef.leftRight)
        } else if (button.name == 'BtnUD') {
            this._machine.enter(StateDef.upDown)
        }
    }

    protected onDestroy(): void {
        TimerMgr.inst.clear(this)
    }


    private update(dt: number) {
        this._machine.update(dt)
    }
}

编辑UIMain界面,创建一个名为BtnStateMachine文本为状态机的按钮。

状态机按钮

编辑scripts/UIMain.tsonButtonClick方法添加如下代码。

case 'BtnWait':
    Wait.show()
    TimerMgr.inst.add(3000, () => Wait.hide())
    break

case 'BtnStateMachine':
    PanelMgr.show(PanelStateMachine)
    break

运行程序,点击状态机按钮,点击左右运动上下运动切换状态。

郁闷,代码里注释的部分即状态机退出时如果关闭键盘事件监听就会出问题,比如上下运动就监听不到键盘事件了。不知道是我理解出问题了还是 Cocos Creator 这里有问题。

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!