###前言:
这次的状态机插件是和 大佬toddlxt 一起肝的233。
前前后后经过多少个日日夜夜激烈的讨论红红火火恍恍惚惚,觉得可以把插件放出来了,请各位父老乡亲,牛鬼蛇神在使用或者测试的时候,手下留情,积极提issue QAQ 。
###Q&A:
####Q1:这次的状态机插件和之前一版的状态机插件有何异同?
实际上之前已经做了一版状态机的插件了,processOn 可视化状态机编辑插件
相同的地方不用说,都是支持可视化绘制状态机。
不同的地方有一下几点:
1:内置状态机绘制功能,使用绘图库gojs实现绘图功能,不再需要借助processOn绘制平台导出的pos文件解析状态机。
2:实现工作现场保存和恢复功能,通过导出FlowForGojs.json配置文件保存当前工作现场,适当的时候只需要将FlowForGojs.json配置文件拖拽回界面就会自动恢复工作现场。
3:支持可视化添加状态机回调,可以直接在插件界面完成回调的添加工作,不再需要在代码手动添加回调。
4:支持数组类型的回调,原有的状态机库重写至typescript格式,并且扩展了回调实现,支持在同一个钩子位置插入多个回调函数,回调顺序和数组中的排序有关。
5:支持以cc.Component.EventHandler的方式添加回调,可以在inspector上可视化添加来自其他脚本的回调方法,实现回调的代理。(慎用,你会哭的,我跟你讲)
6:自动生成代码的空回调,不需要手动写回调的签名。
7:自动生成状态名和事件名的代码智能提示,降低手写状态名和事件名的拼写出错概率。
8:废除原有状态机库的async异步机制,所有回调函数的返回值都是void类型,异步函数的代替方案建议用typescript的promise。
9:插件运行在浏览器,没有集成到creator中,也没有依赖node.js任何库,加速插件启动,方便必要时进行多窗口多状态机编辑。
####Q2:新插件的使用方式?
首先到github下载插件:state-machine-editor-for-ccc-ts
下载后解压,双击index.html启动插件,进入插件主界面。
具体的使用方法可以看左下角的提示,按alt + h 打开帮助界面。
这里通过制作一个简单的状态机来具体地介绍插件的使用方式。
1.在左边的绘制界面双击创建状态。
2.双击状态改名为stateA, (如果状态已经聚焦,只需要再单击就可以改名)
3.同样的方式创建状态,改名stateB
4.按下快捷键alt + q 切换至连线模式 (左下角显示为link mode表示处于连线模式下)
5.按住stateA指向stateB创建有向线段。
6.同样的方式修改事件的名字为eventA2B。
7.选中stateA,在右侧的回调编辑界面 点击enter数组下,(empty array)前面的灰色镂空方框,然后点击Append菜单项。
8.添加回调名字为enterA
9.同样的方式为stateA添加enterA2,leaveA,leaveA2回调。
10.同样的方式为eventA2B和stateB添加回调。(记得先选中目标事件或目标状态来刷新回调编辑界面)
11.点击画布的空白处,让所有状态和事件失去焦点,此时回调编辑器将可以编辑全局回调。
12.同样的方式添加全局回调。
13.选择stateA 按下快捷键alt + c ,使stateA为状态机的初始状态。
- 按下快捷键alt + s 弹出导出界面。
这里主要有四个选项。StateMachine.ts是经过重写的typescript状态机库,FsmImplClass.ts是我们刚刚绘制的状态机的代码,FlowForGojs.json是当前工作现场的配置数据,必要时可以用来恢复状态机工作现场。all是导出前面三项。
15.单击all按钮,弹出状态机代码的类名配置界面(为空则使用默认类名FsmImplClass)。
16.单击confirm按钮,同意浏览器导出多份文件的验证后,拿到上述三份文件。
17.首先演示工作现场恢复的功能,先刷新当前插件界面。
18.然后将FlowForGojs.json文件拖拽回插件界面就会自动恢复现场。
19.接下来打开cocos creator 新建一个空白项目,新建一个空场景,将StateMachine.ts和FsmImplClass.ts扔进cocos creator资源管理器里面。
20.双击脚本FsmImplClass.ts 进入代码编辑器,找到FsmImplClass这个类,在回调里面打log。
21.添加onLoad方法,在里面启动状态机和发射事件eventA2B。
22.回到编辑器,将脚本FsmImplClass.ts挂到场景中。
23.预览游戏,按f12打开浏览器开发者控制台,可以看到状态机的回调log。
24.下面演示通过cc.ComponentEventHandler代理回调的写法。我们以回调enterB为例。
25.回到cocos creator编辑器,新建一份typescript脚本,改名为DelegateTest。
26.为DelegateTest.ts添加一个回调函数。
27.回到cocos creator编辑器,将脚本挂到场景中。
28.将DelegateTest的回调挂到FsmImplClass的EventHandler中。
29.再次预览游戏,按f12打开开发者控制台,可以看到代理回调的log。
####Q3:有什么注意事项和细节?
1.状态机中状态以及事件是不允许同名的。虽然看起来很合理,但是请不要这么做。
2.在回调中会拿到4个参数,分别是事件eventName,起点状态from,终点状态to,参数数组…args,如果这个回调是以cc.Component.EventHandler代理出去的话,在inspector界面可以传customEventData,那么这个customEventData将存在args数组的最后一项args[args.length - 1] 。
3.这个状态机使用的原有库的github链接:javascript-state-machine,虽然这里使用的状态机库StateMachine.ts是原有库的魔改版本,不过读者可以在原作者的readme中了解到这个状态机库的一些基本概念。
4.一个事件的触发会引起状态变换。对应的回调顺序为beforeEvent=>leaveState=>enterState=>afterEvent。
5.原有库提供了一些方法来确定回调的当前状态,
fsm.can(eventName):是否可以发射事件eventName,
fsm.cannot(eventName):是否不能发射事件eventName,
fsm.is(stateName):当前状态是不是stateName,
fsm.current:当前状态。
这里魔改成typescript版本后,对应的回调为:
this.fsmCan(eventName),
this.fsmCannot(eventName)
this.fsmIs(stateName)
this.fsmCurrent()
至于stateName和eventName
则可以直接通过this.stateName和this.eventName获得。
6.除了通过this.eventNameXXX()的方式触发回调,也可以通过this.fsmTrigger(this.eventName.XXX)触发回调,两种方法的效果是一样的。
7.全局回调并不针对某个状态,实际上全局回调监听的是所有的状态和事件的变换。
8.通过this.fsmStartUp()方法会让状态机从一个隐藏的none状态,通过隐藏的startup事件,进入指定的initial状态。从而完成状态机的初始化,通常这个过程不会被发现,只有全局的回调会监听到这个不为人知的{ none,startup }=>initial过程。
9.如果报错提示事件不能从当前状态触发,一是你的逻辑问题,二是你忘记了给状态机设置initial状态,到抽象类Fsm的fsmStartUp里面的StateMachine.create({initial:""})补上initial的值就可以了。
10.请尽量少用cc.Component.EventHandler进行回调的代理,在正常的回调里再调用外面的函数会是一种更好的方式。否则你在debug状态机的时候将毫无头绪。
####Q4:为什么快捷键这么蛋疼?
现在竞争这么激烈,快捷键没剩几个了,键盘就这么大,我能怎么样我,也很绝望呀 QAQ 。
另外绘图是基于GoJs的,相关的快捷键进去搜 Keyboard Shortcuts GoJsAPI:
####Q5:发现了bug应该怎么做?
到github这里提issue。
##更新【20170712】
1.支持同名事件。
2.合并flow.json和fsm,ts文件。修改导出的状态机只需要把fsm.ts扔到插件界面中。
3.剔除对cc.Component.EventHandler的支持。
4.需要自行继承导出的状态机代码fsm.ts 并且使用ide自带的abstract generate功能自动生成状态机回调的空方法。
5.这个是我为ciga特制的版本,虽然最后没有用到。.