cocos creator 可视化状态机插件(支持typescript)

###前言
这次的状态机插件是和 大佬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为状态机的初始状态。

  1. 按下快捷键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特制的版本,虽然最后没有用到。.

22赞

赞 

大佬,收徒弟不。。。:hamburger:

1赞

强大~~~~~ 期待更新呀~~~
如果我先写了一半,然后再要更新状态机, 现在的实现貌似代码同步很痛苦啊。。

过段时间会把同名事件的功能补上,更新状态机这个也已经有方案可以把fsmForGojs.json和fsm.ts合并,另外到时可能会把这个插件集成到 creator 里面,方便直接把代码输出到项目目录下面,代码写一半然后更新状态机这个也有方案,就是自动生成abstract的空implements 我还没去找vsCode有没相关插件来实现这个,按道理是有的,没有的话到时得看看怎么撸vsCode插件了。最后会考虑加个定制模板的功能,用于根据需要定制导出的状态机代码。等这阵子忙完吧。。

生成代码不用vscode插件,直接用ccc的插件系统就可以了。

可以在ts中将继承自抽象父类的抽象方法自动生成空的implements方法的ccc插件嘛? 我还没留意过。。

不太清楚你具体想要做成什么样的。但是ccc的插件系统,单纯的做生成代码是可行的

怎么产生单例啊
好几个地方都引用了状态机后
状态是共享的吗
多个地方引用就会有多次触发onLoad
每个里面都this.fsmStartUp();
不知道是不是单例啊

1赞

mac chrome 快捷键一个都用不了

我回头查一下是不是bug。。至于是不是单例这属于两难的设计问题。我会尽量给一个能覆盖所有需求的设计。这两天在忙。。完了我把顺便把版本更新下。。感谢支持。。

感谢反馈。。我没在mac测试过。。我回头再测试下。。等这两天忙完

我认为,状态机不应该共享。对于状态机的操作应该集中在一个脚本中。由这个初始化了状态机的脚本再开放必需的public方法来给外面用。会是一个更好的设计

没有 config 文件导出

只有两个 ts 文件 没有 json 文件导出

推!
看了下javascript state machine源碼有支援複合狀態
希望之後編輯器也可以支援畫複合狀態:relaxed:

请问chrome for mac 快捷键无效,怎么解决?

不管怎么说,看到了就mark一下