深入 Cocos Creator 3.0 的插件擴展系統(tǒng)
上周我們集結(jié)引擎組所有功能開發(fā)的一線大佬,開辟「Cocos Creator 3.0 技術(shù)專欄」,并推出了第一篇文章 Cocos Creator 3.0 TypeScript 使用答疑,廣受好評。
在此也提醒上篇留言中收到獲獎消息的同學盡快回復(fù)收件地址,以便 C 姐安排寄出禮品噢。
3.0 合并了原有 2D 和 3D 兩套產(chǎn)品的所有功能,延續(xù)了 Cocos 在 2D 品類上輕量高效的優(yōu)勢,并且為 3D 重度游戲提供高效的開發(fā)體驗。
本篇為 3.0 系列專欄第二篇文章,將為大家詳細介紹 Cocos Creator 3.0 插件系統(tǒng),作者 sijie。
如果去年你有在我們的沙龍現(xiàn)場,可能見過他。如果不在,希望下次你在。歡迎閱讀~
插件系統(tǒng)
從 Creator 第一個版本,就已支持編寫擴展插件。但那時候的擴展插件并沒有太多的能力。
經(jīng)過 1.x、2.x 的迭代,插件系統(tǒng)也逐漸地開放了不少功能。例如構(gòu)建擴展、場景擴展。
但這始終是飲鳩止渴,因為一些原因,還是有許多需求無法擴展或者不容易擴展。
直到 19 年,我們針對編輯器進行了大規(guī)模的重構(gòu),其中的重中之重就是編輯器的擴展能力。這是一個艱難的決定,但卻是不得不走出的一步。
在這次修改里明確了所有功能都以插件為單位劃分,一個插件就是一個功能。功能的調(diào)用也簡化成了擴展插件間的通信,并隱藏了多余的內(nèi)部細節(jié),提供了統(tǒng)一的功能擴展機制。
插件的結(jié)構(gòu)
每個插件都是一個完整的功能模塊。包含了:
插件主要邏輯 一個或者多個面板 注冊需要使用的其他功能模塊配置
他們都通過 package.json 進行注冊。我們先來看看 package.json 的格式:
{
"name": "test-extension",
"package_version": 2,
"main": "./dist/main.js",
"panels": {},
"contributions": {}
}
name 擴展插件的名稱,我們推薦遵循 npm 命名規(guī)范。package_version 使用的系統(tǒng)版本號,需要填寫固定的數(shù)字 2。main 插件的邏輯入口 panels 需要注冊的面板 contributions 使用的其他功能。
更多詳細介紹:
https://docs.cocos.com/creator/manual/zh/
簡化的通信機制
當擴展插件間需要互相調(diào)用的時候,我們提供了一套相較 2.x 更為簡易的消息機制。
插件間只有兩種消息操作方式,一種是一對一操作:
// 發(fā)送后不再管返回數(shù)據(jù)
Editor.Message.send(extensionName, messageName, ...args);
// 發(fā)送后等待數(shù)據(jù)返回,并通過 promise 傳遞出來
const result = await Editor.Message.request(extensionName, messageName, ...args);
另一種是一對多操作:
// 向所有插件發(fā)送一個通知
Editor.Message.broadcast('scene:change-node', dump);
在新的消息機制里,我們只需要關(guān)心需要使用哪一個功能里的什么方法。例如 "屬性檢查器" 要去 "場景編輯器" 查詢當前選中的節(jié)點信息。
// 向 scene 發(fā)送 query-node 請求,并等待返回
const info = await Editor.Message.request('scene', 'query-node', uuid);
但使用上簡化后,帶來的是注冊機制上的復(fù)雜化,我們在注冊消息的時候,也是以插件為單位的,當插件收到消息后,自主的選擇需要轉(zhuǎn)發(fā)給主入口處理,還是某個面板處理:
{
"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 消息的時候,會轉(zhuǎn)發(fā)到 "./dist/main.js" 里定義的 methods 上的 record 方法。
當擴展插件收到 to-panel 消息的時候,會轉(zhuǎn)發(fā)到 ".//dist/panels/default/index.js" 里定義的 methods 上的 record 方法。觸發(fā)方法是一個數(shù)組,當然也可以同時發(fā)給多個位置。
而編輯器內(nèi)已經(jīng)開放的消息,則可以通過 "開發(fā)者" - "消息管理器" 進行查詢,我們?nèi)绻M_放自己的消息給其他人使用,在注冊 message 的時候,加上 public 屬性,值為 true,就可以自動顯示在這個面板上。

統(tǒng)一的注冊方式
剛剛我們已經(jīng)在 package.json 里的 contributions 里寫了 message 數(shù)據(jù)。這里的意思就是當前擴展插件使用了 message 功能,并根據(jù) message 功能約定的格式,提供了數(shù)據(jù)給 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"
}
]
}
}
這樣注冊之后,就會在頂部菜單的 "節(jié)點" 里,多一個 test 按鈕。按下按鈕后,就會發(fā)出一條 "print-test-log" 的消息,最終觸發(fā) "./dist/main.js" 里的 methods.printTestLog。
是不是有點兒繞?我們先來了解為什么需要繞這么一大圈。
在新的插件系統(tǒng)里,所有的操作都是通過 message 進行交互,當按下菜單的時候,觸發(fā)了一個 "消息",當按下快捷鍵的時候,觸發(fā)了另一個 "消息"。
這樣就統(tǒng)一了整個編輯器內(nèi)的交互方式,只要知道了消息內(nèi)容和數(shù)據(jù)格式,那么我們就能夠調(diào)用任意一個功能里的方法,當然前提是這個功能開放了允許大家操作的 "消息" 接口。
message 就相當于每個功能模塊的 api,所以所有的操作都直接調(diào)用了 message。而整個插件機制,都是基于 contributions 和 message 兩個機制。
已經(jīng)開放的功能
目前已經(jīng)開放的功能有:
消息通信 快捷鍵 場景腳本 主菜單 構(gòu)建 資源管理器
更多詳細功能:
https://docs.cocos.com/creator/manual/zh/
常見問題
在自己的擴展插件里,面板怎么和插件主入口交互
通過 message 進行轉(zhuǎn)發(fā),發(fā)送給自己,然后轉(zhuǎn)到主入口,通過 message 機制進行通訊。
在不同的文件內(nèi) require 同一個 js 腳本,但里面的數(shù)據(jù)并沒有共享
面板與面板,面板與插件主入口分別運行在不同進程。這些進程是相互隔離的,如果需要交互數(shù)據(jù)。
有一些 electron 接口,在某個版本就突然無法使用了
我們不建議大家直接使用 electron 接口,如果有功能,編輯器沒有封裝,并且一定需要使用,可以在論壇進行反饋。
因為 electron 接口可能隨著版本升級被棄用甚至刪除,將可能導(dǎo)致插件運行不正常的情況。
想寫一個類似 inspector 的界面,但編輯器沒有給更多文檔怎么辦
之前說到,編輯器內(nèi)交互都通過了 message 機制,那我們可以通過 "開發(fā)者" - "消息調(diào)試工具" 抓取編輯器內(nèi)某個操作的消息,模仿發(fā)送就能夠?qū)崿F(xiàn)類似的功能。
例如下圖,打開調(diào)試工具后,點擊開始,修改一次 inspector,可以看到抓取到了許多操作,其中數(shù)據(jù)修改操作就是 set-property。

升級插件
大家肯定想問,那我們在 2.x 寫的插件還能夠正常運行么?
其實只需要稍加修改,就能夠運行了。
package.json 修改
將插件主入口、面板入口文件里的 messages,改成 methods。然后在 package.json 里新增一個 contributions 對象。進行一次轉(zhuǎn)發(fā)。
接著將 panel 定義數(shù)據(jù)修改成新的格式,寫到 package.json 里的 panels 對象數(shù)據(jù)里。
替換原先的編輯器接口
搜索插件內(nèi)的 Editor 字符串,比對接口,換用 3.x 的接口。
我們可以在 3.x 編輯器里,在頂部菜單里選擇導(dǎo)出 dts 選項,導(dǎo)出編輯器的接口定義文件。一般情況下,2.x 的接口都能夠在 3.x 里找到對應(yīng)的方法。
最大的改動是使用了 promise 替換了所有的 callback。message 處理函數(shù)里,不再有 event,不需要調(diào)用 event.reply 方法。
整理布局
因為一些默認樣式的修改,可能會有部分界面顯示錯亂。我們整理了一份默認 ui 組件的示例,在頂部菜單 "開發(fā)者" - "ui 組件" 里。部分 css 可能需要進行調(diào)整。
使用新的注冊功能方式
這部分改動比較大,例如構(gòu)建插件的勾子。在 3.x 里的用法,和 2.x 完全不一樣,所以需要使用新的方法進行注冊。
以上就是關(guān)于 Cocos Creator 3.0 插件系統(tǒng)的詳細講解,后續(xù)本專欄將不定期更新,為大家?guī)戆ǖ幌抻谝韵聝?nèi)容:
《Cocos Creator 3.0 里如何玩轉(zhuǎn) npm 海量資源》by 放空
《Cocos Creator 3.0 的 3D 物理講解》by Jayce Lai
《Cocos Creator 3.0 的資源系統(tǒng)》by Santy Wang
......
留言告訴我們其他你想看的內(nèi)容,如果你有想要的功能以及好的想法,也歡迎在留言處或前往論壇(鏈接)一起討論噢~
往期精彩


