新建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.ts
,onButtonClick
方法添加如下代码。
case 'BtnWait':
Wait.show()
TimerMgr.inst.add(3000, () => Wait.hide())
break
case 'BtnStateMachine':
PanelMgr.show(PanelStateMachine)
break
运行程序,点击状态机
按钮,点击左右运动
和上下运动
切换状态。
郁闷,代码里注释的部分即状态机退出时如果关闭键盘事件监听就会出问题,比如上下运动
就监听不到键盘事件了。不知道是我理解出问题了还是 Cocos Creator 这里有问题。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。