查看原文
其他

深入 Cocos Creator 3.0 的插件扩展系统

sijie COCOS 2022-06-10

上周我们集结引擎组所有功能开发的一线大佬,开辟Cocos Creator 3.0 技术专栏,并推出了第一篇文章 Cocos Creator 3.0 TypeScript 使用答疑,广受好评。

在此也提醒上篇留言中收到获奖消息的同学尽快回复收件地址,以便 C 姐安排寄出礼品噢。

3.0 合并了原有 2D 和 3D 两套产品的所有功能,延续了 Cocos 在 2D 品类上轻量高效的优势,并且为 3D 重度游戏提供高效的开发体验。

本篇为 3.0 系列专栏第二篇文章,将为大家详细介绍 Cocos Creator 3.0 插件系统,作者 sijie

如果去年你有在我们的沙龙现场,可能见过他。如果不在,希望下次你在。欢迎阅读~


插件系统


从 Creator 第一个版本,就已支持编写扩展插件。但那时候的扩展插件并没有太多的能力。

经过 1.x、2.x 的迭代,插件系统也逐渐地开放了不少功能。例如构建扩展、场景扩展。

但这始终是饮鸠止渴,因为一些原因,还是有许多需求无法扩展或者不容易扩展。

直到 19 年,我们针对编辑器进行了大规模的重构,其中的重中之重就是编辑器的扩展能力。这是一个艰难的决定,但却是不得不走出的一步。

在这次修改里明确了所有功能都以插件为单位划分,一个插件就是一个功能。功能的调用也简化成了扩展插件间的通信,并隐藏了多余的内部细节,提供了统一的功能扩展机制。


插件的结构


每个插件都是一个完整的功能模块。包含了:

  1. 插件主要逻辑
  2. 一个或者多个面板
  3. 注册需要使用的其他功能模块配置

他们都通过 package.json 进行注册。我们先来看看 package.json 的格式:

{
"name": "test-extension",
"package_version": 2,
"main": "./dist/main.js",
"panels": {},
"contributions": {}
}

name 扩展插件的名称,我们推荐遵循 npm 命名规范。package_version 使用的系统版本号,需要填写固定的数字 2。main 插件的逻辑入口 panels 需要注册的面板 contributions 使用的其他功能。

更多详细介绍:

https://docs.cocos.com/creator/manual/zh/


简化的通信机制


当扩展插件间需要互相调用的时候,我们提供了一套相较 2.x 更为简易的消息机制。

插件间只有两种消息操作方式,一种是一对一操作:

// 发送后不再管返回数据
Editor.Message.send(extensionName, messageName, ...args);
// 发送后等待数据返回,并通过 promise 传递出来
const result = await Editor.Message.request(extensionName, messageName, ...args);

另一种是一对多操作:

// 向所有插件发送一个通知
Editor.Message.broadcast('scene:change-node', dump);

在新的消息机制里,我们只需要关心需要使用哪一个功能里的什么方法。例如 "属性检查器" 要去 "场景编辑器" 查询当前选中的节点信息。

// 向 scene 发送 query-node 请求,并等待返回
const info = await Editor.Message.request('scene''query-node', uuid);

但使用上简化后,带来的是注册机制上的复杂化,我们在注册消息的时候,也是以插件为单位的,当插件收到消息后,自主的选择需要转发给主入口处理,还是某个面板处理:

{
"name": "test-extension",
"package_version": 2,
"main": "./dist/main.js",
"panels": {
"default": {
"main": "./dist/panels/default/index.js"
}
},
"contributions": {
"message": {
"to-main": {
"methods": ["record"]
},
"to-panel": {
"methods": ["default.record"]
}
}
}
}

当扩展插件收到 to-main 消息的时候,会转发到 "./dist/main.js" 里定义的 methods 上的 record 方法。 

当扩展插件收到 to-panel 消息的时候,会转发到 ".//dist/panels/default/index.js" 里定义的 methods 上的 record 方法。触发方法是一个数组,当然也可以同时发给多个位置。

而编辑器内已经开放的消息,则可以通过 "开发者" - "消息管理器" 进行查询,我们如果希望开放自己的消息给其他人使用,在注册 message 的时候,加上 public 属性,值为 true,就可以自动显示在这个面板上。


统一的注册方式


刚刚我们已经在 package.json 里的 contributions 里写了 message 数据。这里的意思就是当前扩展插件使用了 message 功能,并根据 message 功能约定的格式,提供了数据给 message 功能使用。 

其他功能也是通过类似的机制进行注册,例如我们新增一个顶部菜单按钮:

{
"name": "test-extension",
"package_version": 2,
"main": "./dist/main.js",
"panels": {},
"contributions": {
"message": {
"print-test-log": {
"methods": ["printTestLog"]
}
},
"menu": [
{
"path": "i18n:menu.node",
"label": "test",
"message": "print-test-log"
}
]
}
}

这样注册之后,就会在顶部菜单的 "节点" 里,多一个 test 按钮。按下按钮后,就会发出一条 "print-test-log" 的消息,最终触发 "./dist/main.js" 里的 methods.printTestLog。

是不是有点儿绕?我们先来了解为什么需要绕这么一大圈。

在新的插件系统里,所有的操作都是通过 message 进行交互,当按下菜单的时候,触发了一个 "消息",当按下快捷键的时候,触发了另一个 "消息"。

这样就统一了整个编辑器内的交互方式,只要知道了消息内容和数据格式,那么我们就能够调用任意一个功能里的方法,当然前提是这个功能开放了允许大家操作的 "消息" 接口。

message 就相当于每个功能模块的 api,所以所有的操作都直接调用了 message。而整个插件机制,都是基于 contributions 和 message 两个机制。


已经开放的功能

目前已经开放的功能有:

  1. 消息通信
  2. 快捷键
  3. 场景脚本
  4. 主菜单
  5. 构建
  6. 资源管理器

更多详细功能:

https://docs.cocos.com/creator/manual/zh/


常见问题


在自己的扩展插件里,面板怎么和插件主入口交互

通过 message 进行转发,发送给自己,然后转到主入口,通过 message 机制进行通讯。

在不同的文件内 require 同一个 js 脚本,但里面的数据并没有共享

面板与面板,面板与插件主入口分别运行在不同进程。这些进程是相互隔离的,如果需要交互数据,需要使用 message 机制。

有一些 electron 接口,在某个版本就突然无法使用了

我们不建议大家直接使用 electron 接口,如果有功能,编辑器没有封装,并且一定需要使用,可以在论坛进行反馈。

因为 electron 接口可能随着版本升级被弃用甚至删除,将可能导致插件运行不正常的情况。

想写一个类似 inspector 的界面,但编辑器没有给更多文档怎么办

之前说到,编辑器内交互都通过了 message 机制,那我们可以通过 "开发者" - "消息调试工具" 抓取编辑器内某个操作的消息,模仿发送就能够实现类似的功能。

例如下图,打开调试工具后,点击开始,修改一次 inspector,可以看到抓取到了许多操作,其中数据修改操作就是 set-property。


升级插件


大家肯定想问,那我们在 2.x 写的插件还能够正常运行么?

其实只需要稍加修改,就能够运行了。


package.json 修改


将插件主入口、面板入口文件里的 messages,改成 methods。然后在 package.json 里新增一个 contributions 对象。进行一次转发。

接着将 panel 定义数据修改成新的格式,写到 package.json 里的 panels 对象数据里。


替换原先的编辑器接口


搜索插件内的 Editor 字符串,比对接口,换用 3.x 的接口。

我们可以在 3.x 编辑器里,在顶部菜单里选择导出 dts 选项,导出编辑器的接口定义文件。一般情况下,2.x 的接口都能够在 3.x 里找到对应的方法。

最大的改动是使用了 promise 替换了所有的 callback。message 处理函数里,不再有 event,不需要调用 event.reply 方法。


整理布局


因为一些默认样式的修改,可能会有部分界面显示错乱。我们整理了一份默认 ui 组件的示例,在顶部菜单 "开发者" - "ui 组件" 里。部分 css 可能需要进行调整。


使用新的注册功能方式


这部分改动比较大,例如构建插件的勾子。在 3.x 里的用法,和 2.x 完全不一样,所以需要使用新的方法进行注册。


以上就是关于 Cocos Creator 3.0 插件系统的详细讲解,后续本专栏将不定期更新,为大家带来包括但不限于以下内容:

  • 《Cocos Creator 3.0 里如何玩转 npm 海量资源》by 放空

  • 《Cocos Creator 3.0 的 3D 物理讲解》by Jayce Lai 

  • 《Cocos Creator 3.0 的资源系统》by Santy Wang

......

留言告诉我们其他你想看的内容,如果你有想要的功能以及好的想法,也欢迎在留言处或前往论坛(链接)一起讨论噢


往期精彩

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存