开发 COCOS Creator 插件的较佳实践

前言

cocos creator 编辑器是由一系列的插件组装而成的,在 creator 底层有一套完善的插件机制,它们可以相互通信,编辑器界面可以自由拼装。

如图,这是一个常见的编辑器界面,它是由若干个插件一起拼装起来的。

所以要拓展编辑器,就是要学会如何编写编辑器插件,详细教程可以查看官方教程

阅读此文,我们假设你已经熟悉了 creator 插件开发流程,知道通过插件可以拓展哪些编辑器的能力。所以本文不会对如何编写插件做太详细的介绍。

本文将只围绕着,如何让编写编辑器插件 更简单更符合人体工学 ,以及 享受当前时代 web 工程化的所有成果

当前的痛点

编辑器插件支持渲染面板,也就是在 package.json 里声明的 panels 的配置。比如:

{
    "name": "console",
    "version": "1.0.0",
    "description": "i18n:console.description",
    "main": "./dist/browser.js", // 主程序

    "panels": {
        "default": {
            "main": "./dist/panel.js" // 面板
        }
    },

    "contributions": {
        "footer": {
            "left": "./dist/footer/left.js", // 拓展
            "right": "./dist/footer/right.js" // 拓展
        }
    }
}

可以看出,一个面板的入口文件是一个 js 文件。配置中是 panel.js。

我们来看下一个 panel 文件的结构:

export const template = '<div id="app"> ...other more html </div>';

export const style = '<style> #app {...other more css} </style>';

export const $ = { root: '#app' };

export function update() {}

export function ready() {}

export function close() {}

这和大家平时做的 web 项目有点区别。 常见的 web 项目,一般渲染一个面板是声明一个 HTML 的入口文件。

而且 HTML 文件天然的支持 css + html + javascript 混合导入。

<html>
    <style>
        /* css */
    </style>
    <body>
        <div>...other more html</div>
    </body>
    <script>
        // js;
    </script>
</html>

编辑器底层的插件系统将入口文件设计成了一个 js 文件,然后将这个 js 文件在封装好的 panel-frame web-component 里去做渲染。目的是更好的管理面板的生命周期,样式隔离,也可以更好的做通信。

带来的问题就是没有很好的案例告诉大家如何组织 html + css + js 。以及如何接入现代化的前端工程来开发编辑器插件。

如果采用入口文件为 html 的方式,则会将每个面板以 iframe 的方式去渲染,这样会导致消息通信更复杂。

主进程 - 渲染进程 - iframe VS 主进程 - 渲染进程(web-component)

少了一层桥接的过程。

现在,我们明白了编辑器的插件为何如此设计,也明白了这样设计带来的好处和开发的痛点。

当前都是怎么做的

常规写法:

export const template = '<div id="app"> ...other more html </div>';

export const style = '<style> #app {...other more css} </style>';

export const $ = { root: '#app' };

export function update() {}

export function ready() {}

export function close() {}

如果插件足够简单,这样做也没什么问题,但是一般插件面板都挺复杂的,复杂度经常大于一般 web 应用。 所以也见过这样来组织 panel 的:

import { join } from 'path';
import { readFileSync } from 'fs';

export const template = readFileSync(join(__dirname, 'path/to/panel.html'), 'utf8');

export const style = readFileSync(join(__dirname, 'path/to/panel.css'), 'utf8');

export const $ = { root: '#app' };

export function update() {}

export function ready() {}

export function close() {}

这样从一定程度上将代码进行了划分,职责更清晰了,但是还是采用命令式的方式去写页面。(注意:这样还在渲染进程使用了 node 的 api,在 electron 的最佳实践是不推荐的)

更有追求的小伙伴会引入 vue 来享受声明式的写法,用数据驱动页面。

如下:

import { join } from 'path';
import { readFileSync } from 'fs';
import Vue from 'vue';

export const template = '<div id="app"></div>';

export const style = readFileSync(join(__dirname, 'path/to/panel.css'), 'utf8');

export const $ = { root: '#app' };

export function update() {}

export function ready() {
    // 看我,它还是 html 分离的方式,没有采用单文件
    const vueTpl = readFileSync(join(__dirname, 'path/to/panel.html'), 'utf8');

    new Vue({
        el: this.$.root, // 将实例挂载到  #app
        template: vueTpl,
    });
}

export function close() {}

达到这步的同学已经算进阶了,因为它好歹享受到了数据驱动,在控制视图的时候已经更得心应手了。

理想状态是什么样的

以上的例子可以看出,我们都是在以 人为 的方式去适应 panle 的结构,我们需要关注 html ,需要关注 css ,需要关注 js 。

我们希望可以享受组件化的开发,比如单文件 .vue ,以及 .jsx 这样的文件后缀,采用完整的现代化的框架来开发。

如今的前端工程化已经非常成熟,UI 方面有 vue 、react、 angular 等。构建方面有 vite、 webpack、等

下面我们就以 vite + vue 为例子,探索一个完美的插件开发体验。

引入 vite

vite 用来构建 web 应用是非常流行且成熟的方案,而且它官方维护了多套 UI 框架的模板,为我们后期开发自己的 CLI 工具提供了极大的启发 (其实我基本完全抄它的)。

lib 模式

vite 的构建入口是 index.html 文件,而我们的 panel 的入口文件是 xx.js ,所以我们只能使用 vite 提供的 lib 模式。(这样失去了 HMR 的能力)。

基于 lib 模式,我们 vite 配置大概如下:

import { defineConfig } from 'vite';
import { nodeExternals } from 'rollup-plugin-node-externals';

export default defineConfig(({ mode }) => {
    /**
     *  注意事项:
     *  vite 在构建 lib 模式的时候,是没有 dev 服务的,dev 主要用于 web 应用
     *  所以在 package.json 的 scripts 里 dev 和 build 都是执行 vite build
     *  只是在 dev 的脚本里,手动指定了 "--mode development" https://cn.vitejs.dev/guide/env-and-mode.html
     *  然后在 development 模式下,我们配置 watch 的配置
     */
    const isDev = mode === 'development';

    return {
        build: {
            lib: {
                entry: {
                    browser: './src/browser/index.js',
                    panel: './src/panel.js',
                    panel_sub: './src/panel-sub.js',
                },
                formats: ['cjs'],
                fileName: (_, entryName) => `${entryName}.cjs`,
            },
            // 重要: vite 默认将构建出来的 css 命名为 style.css ,但是我们的 panel 可能有多个,需要每个 panel 生成自己的 css 文件,要开启 cssCodeSplit
            cssCodeSplit: true,
            watch: isDev
                ? {
                      include: ['./src/**/*.js', './src/**/*.css'],
                  }
                : null,
            minify: false,
        },
        plugins: [],
    };
});

然后我们的 panel.js 就可以这样写:

import App from './App.vue';
import { createApp } from 'vue';
import { readFileSync } from 'fs';
import { join } from 'path';


const weakMap = new WeakMap();

export default Editor.Panel.define({
    template: '<div id="app"></div>', // 只留一个 div 用于 vue 的挂载

    // 注意: 这边是引入通过 vite 构建且分离出来的 css 文件,一般在 dist 文件夹中
    style: readFileSync(join(__dirname, 'path/to/panel.css'), 'utf8');
    $: {
        root: '#app',
    },
    ready() {
        if (!this.$.root) return;

        const app = createApp(App);
        app.mount(this.$.root);

        weakMap.set(this, app);
    },
    close() {
        const app = weakMap.get(this);
        app?.unmount?.();
    },
});

其实到这步,我们已经可以使用完整的单文件 .vue 来开发面板了,你可以像开发常规 web 应用一样通过组件化去组装你的应用。

但是目前还遗留一个问题,我们在 panel.js 里面去手动引入了 构建出来的css 文件,在源码里引入预期的构建出的产物,是一种反直觉的操作。

编写 vite 插件 @cocos-fe/vite-plugin-cocos-panel

npm 下载地址

我们希望能像 vite 构建 index.html 入口文件一样,自动将 css 等依赖,自动的打入到最终的 index.html 去。

也就是比如我们的 panel.ts 是这样的:

import App from './App.vue';
import { createApp } from 'vue';

const weakMap = new WeakMap();

export default Editor.Panel.define({
    template: '<div id="app"></div>', // 只留一个 div 用于 vue 的挂载
    // 注意看,我们没有写任何的 style
    $: {
        root: '#app',
    },
    ready() {
        if (!this.$.root) return;

        const app = createApp(App);
        app.mount(this.$.root);

        weakMap.set(this, app);
    },
    close() {
        const app = weakMap.get(this);
        app?.unmount?.();
    },
});

通过 vite 构建之后,文件是这样的:

所以我们就需要一个专门为 cocos-plugin 服务的构建插件,该插件需要做 2 件事。

  • 1、 将构建配置的 build.cssCodeSplit 设置为 true

这样才可以对每个入口文件分离出属于自己的 css 样式。

  • 2、 需要将分离出来的 css 样式,自动写入到 panel.js 的 style 属性中去。

在拥有这个插件之后,我们就解决了在源码里面去引入构建产物的问题,且一切都是自动完成。

最终形态

最终,你的 panel.ts 代码将是如下的模样:

vite.config.ts

import { defineConfig } from 'vite';
import { nodeExternals } from 'rollup-plugin-node-externals';
import vue from '@vitejs/plugin-vue';
import { cocosPanelConfig, cocosPanelCss } from '@cocos-fe/vite-plugin-cocos-panel';

export default defineConfig(({ mode }) => {
    /**
     *  注意事项:
     *  vite 在构建 lib 模式的时候,是没有 dev 服务的,dev 主要用于 web 应用
     *  所以在 package.json 的 scripts 里 dev 和 build 都是执行 vite build
     *  只是在 dev 的脚本里,手动指定了 "--mode development" https://cn.vitejs.dev/guide/env-and-mode.html
     *  然后在 development 模式下,我们配置 watch 的配置
     */
    const isDev = mode === 'development';

    return {
        build: {
            lib: {
                entry: {
                    browser: './src/browser/index.ts',
                    panel: './src/panels/panel.ts',
                },
                formats: ['cjs'],
                fileName: (format, entryName) => `${entryName}.${format}`,
            },
            watch: isDev
                ? {
                      include: ['./src/**/*.ts', './src/**/*.vue', './src/**/*.css'],
                  }
                : null,
            target: 'modules',
            minify: false,
        },
        plugins: [
            vue({
                template: {
                    compilerOptions: {
                        isCustomElement: (tag) => tag.startsWith('ui-'),
                    },
                },
            }),
            nodeExternals({
                builtins: true, // 排除 node 的内置模块
                deps: false, // 将依赖打入 dist,发布的时候可以删除 node_modules
                devDeps: true,
                peerDeps: true,
                optDeps: true,
            }),
            cocosPanelConfig(),
            cocosPanelCss(),
        ],
    };
});

panel.ts

import App from './App.vue'; // 你的所有业务逻辑都在这个 app.vue 里面,是个常规的 vue 项目
import { createApp } from 'vue';
import './style.css';

const weakMap = new WeakMap();

export default Editor.Panel.define({
    template: '<div id="app"></div>', // 只留一个 div 用于 vue 的挂载
    $: {
        root: '#app',
    },
    ready() {
        if (!this.$.root) return;

        const app = createApp(App);
        app.mount(this.$.root);

        weakMap.set(this, app);
    },
    close() {
        const app = weakMap.get(this);
        app?.unmount?.();
    },
});

App.vue

<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue';
</script>

<template>
    <div>
        <a href="https://vitejs.dev" target="_blank">
            <img src="./assets/vite.svg" class="logo" alt="Vite logo" />
        </a>
        <a href="https://vuejs.org/" target="_blank">
            <img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
        </a>
        <a href="https://www.cocos.com/" target="_blank">
            <img src="./assets/cocos.png" class="logo cocos" alt="Cocos logo" />
        </a>
    </div>
    <HelloWorld msg="Vite + Vue + Cocos Creator" />
    <ui-input></ui-input>
</template>

<style scoped>
.logo {
    height: 6em;
    padding: 1.5em;
    will-change: filter;
    transition: filter 300ms;
}
.logo:hover {
    filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vue:hover {
    filter: drop-shadow(0 0 2em #42b883aa);
}
</style>

结合第三方 UI 框架

有的同学希望在使用 vue 的同时,可以使用 element-plus 等框架,理论上这些框架在我们支持通过 vite 去构建单文件之后就自然的支持的,但是由于我们的 panle 最终是在一个 web-component 里面渲染的,所以需要对第三方的框架做点处理。

  • css 样式里的 :root 需要改成 :host 这样我们才能在 web-component 里面让 elment-plus 的样式生效我们在 @cocos-fe/vite-plugin-cocos-panel 里提供了 css 的转换钩子,如果你需要使用 element-plus,你需要传入 transform 来转换它,使其能够在 web-component 里正常渲染 。 你可以配置里面加如下的参数:
import { defineConfig } from 'vite';
import { cocosPanelConfig, cocosPanelCss } from '@cocos-fe/vite-plugin-cocos-panel';

export default defineConfig(({ mode }) => {
    return {
        plugins: [
            cocosPanelConfig(), // 调整配置文件
            cocosPanelCss({
                transform: (css) => {
                    // element-plus 的全局变量是作用在 :root , 需要改成 :host
                    // 黑暗模式它是在 html 添加 dark 类名,我们应该在最外层的 #app 添加 class="dark"
                    return css.replaceAll(':root', ':host').replaceAll('html.dark', '#app.dark');
                },
            }),
        ],
    };
});
  • 在使用例如弹窗这样的组件是,需要将 appendTo 设置到当前 panle 的 root 也就是我们 vue 最终挂载到的那个 dom 元素

vite.config.js

import { defineConfig } from 'vite';
import { nodeExternals } from 'rollup-plugin-node-externals';
import vue from '@vitejs/plugin-vue';
import { cocosPanelConfig, cocosPanelCss } from '@cocos-fe/vite-plugin-cocos-panel';
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';

export default defineConfig(({ mode }) => {
    /**
     *  注意事项:
     *  vite 在构建 lib 模式的时候,是没有 dev 服务的,dev 主要用于 web 应用
     *  所以在 package.json 的 scripts 里 dev 和 build 都是执行 vite build
     *  只是在 dev 的脚本里,手动指定了 "--mode development" https://cn.vitejs.dev/guide/env-and-mode.html
     *  然后在 development 模式下,我们配置 watch 的配置
     */
    const isDev = mode === 'development';

    return {
        build: {
            lib: {
                entry: {
                    browser: './src/browser/index.js',
                    panel: './src/panels/panel.js',
                },
                formats: ['cjs'],
                fileName: (format, entryName) => `${entryName}.cjs`,
            },
            watch: isDev
                ? {
                      include: ['./src/**/*.js', './src/**/*.vue', './src/**/*.css'],
                  }
                : null,
            target: 'modules',
            minify: false,
        },
        plugins: [
            vue({
                template: {
                    compilerOptions: {
                        isCustomElement: (tag) => tag.startsWith('ui-'),
                    },
                },
            }),
            nodeExternals({
                builtins: true, // 排除 node 的内置模块
                deps: false, // 将依赖打入 dist,发布的时候可以删除 node_modules
                devDeps: true,
                peerDeps: true,
                optDeps: true,
            }),
            cocosPanelConfig(),
            cocosPanelCss({
                transform: (css) => {
                    // element-plus 的全局变量是作用在 :root , 需要改成 :host
                    // 黑暗模式它是在 html 添加 dark 类名,我们应该在最外层的 #app 添加 class="dark"
                    return css.replaceAll(':root', ':host').replaceAll('html.dark', '#app.dark');
                },
            }),
            AutoImport({
                resolvers: [ElementPlusResolver()],
            }),
            Components({
                resolvers: [ElementPlusResolver()],
            }),
        ],
    };
});

panel.js

import App from './App.vue';
import { createApp } from 'vue';
import './style.css';
import 'element-plus/dist/index.css';
import 'element-plus/theme-chalk/dark/css-vars.css';
import { ElMessage } from 'element-plus';

const weakMap = new WeakMap();

export default Editor.Panel.define({
    template: '<div id="app" class="dark"></div>', // 加了 dark 类名,模拟黑暗模式
    $: {
        root: '#app',
    },
    ready() {
        if (!this.$.root) return;

        const app = createApp(App);
        app.provide('appRoot', this.$.root);
        app.provide('message', (options) => {
            if (typeof options === 'string') {
                options = { message: options };
            }
            options.appendTo = options.appendTo || this.$.root;
            return ElMessage(options);
        });
        app.mount(this.$.root);

        weakMap.set(this, app);
    },
    close() {
        const app = weakMap.get(this);
        app?.unmount?.();
    },
});

App.vue

<script setup>
import HelloWorld from './components/HelloWorld.vue';
import { inject } from 'vue';
import { ElMessage } from 'element-plus';

const appRootDom = inject('appRoot');
const message = inject('message');

const open = () => {
    ElMessage({
        message: 'show message',
        appendTo: appRootDom,
    });
};

function open2() {
    message('show inject message');
}
</script>

<template>
    <div>
        <a href="https://vitejs.dev" target="_blank">
            <img src="./assets/vite.svg" class="logo" alt="Vite logo" />
        </a>
        <a href="https://vuejs.org/" target="_blank">
            <img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
        </a>
        <a href="https://www.cocos.com/" target="_blank">
            <img src="./assets/cocos.png" class="logo cocos" alt="Cocos logo" />
        </a>
        <a href="https://element-plus.org/zh-CN/" target="_blank">
            <img src="./assets/element-plus-logo.svg" class="logo element" alt="element-plus logo" />
        </a>
    </div>
    <HelloWorld msg="Vite + Vue + Cocos Creator + element-plus" />
    <el-button type="primary" @click="open"> show message </el-button>
    <el-button type="danger" @click="open2"> show inject message </el-button>
</template>

<style scoped>
.logo {
    height: 6em;
    padding: 1.5em;
    will-change: filter;
    transition: filter 300ms;
}
.logo:hover {
    filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vue:hover {
    filter: drop-shadow(0 0 2em #42b883aa);
}
</style>

create-cocos-plugin

以上,我们介绍了如何使用单文件 .vue 来编写编辑器面板,也介绍了我们封装的 vite 插件 @cocos-fe/vite-plugin-cocos-panel 用于自动挂载样式到 panel 上。

为了方便大家快速使用,我们还准备了几个预设的模板。大家可以创建一个本地插件来体验下。

create-cocos-plugin 是我们提供的 CLI 工具。它几乎完全借(chao)鉴(xi) 了 vite 的 create-vite

你只需要在终端里执行

npm create cocos-plugin@latest

就可以快速选择一个我们配置好的模板,进行插件的开发。

视频地址: https://cocos-creator.github.io/fe-team/assets/video.DLWmmyqV.mov"

24赞

666,终于有官方支持了。

刚好在用element-plus + vite + electron写一个工具,配合上AI要一些简单插件就是问AI一句话的事 :partying_face: :partying_face: :partying_face:

各位大佬们可以踊跃提意见,后续我们整合方案后会融入 creator 中,作为模板创建的基础功能,降低大家开发插件的困难。

想要一个用 cocos creator 的 prefab + scene 这些概念做的一个 cocos creator 插件的教程

  1. 配置过于繁杂

  2. 脚本逻辑不能热插拔,例如主进程脚本必须重启编辑器

  3. 场景脚本、主进程、渲染进程,增加了开发复杂度
    光是理解这三个都阻拦了一大批人开发插件,或者导致未理解时开发的程序 bug,例如在主进程或者渲染进程 import cc

对于插件开发简化我之前做过尝试,还算比较成功,可以参考:秀下工作成果:QuickPlugin,一个文件即可创建插件,颠覆插件开发

但是对于理解进程这个对于开发者还是硬伤,必须清楚才不会写出 bug

这个我做过,就是用 iframe 嵌套就行了

我也是刚刚接触electron和vue,有ai的帮忙进度提升了非常多,差不多80%的代码都是AI写的 :joy: :joy: :joy:

写了这么长的文章来介绍这么拙劣的插件开发方式,我只能说,真有你们的。但凡看看unity编写插件的方式就能明白你们如此晦涩难懂的玩意究竟是不是给用户用的。复杂点的,unity可以写出自己的编辑器,简单点的比如给Hierarchy的特殊节点加个边框。

   [InitializeOnLoad]
   public class HighlightHiddenObjects
   {
       static HighlightHiddenObjects()
       {
           EditorApplication.hierarchyWindowItemOnGUI += OnHierarchyGUI;
       }

       private static void OnHierarchyGUI(int instanceID, Rect selectionRect)
       {
           GameObject gameObject = EditorUtility.InstanceIDToObject(instanceID) as GameObject;

           if (gameObject != null)
           {
               if (SceneVisibilityManager.instance.IsHidden(gameObject))
               {
                   Rect outlineRect = new(selectionRect.x - 2, selectionRect.y, selectionRect.width + 3, selectionRect.height);

                   Handles.BeginGUI();
                   Handles.color = Color.white;
                   Handles.DrawWireCube(outlineRect.center, outlineRect.size);
                   Handles.EndGUI();
               }
           }
       }
   }

你们这玩意的架构几乎难以让插件简单开发。成本之大,难以想象。不说unity,距离laya的编辑器插件实现都不如啊。看看这长篇大论,满篇都是在介绍web。满篇都是在hack。但凡是个正常游戏开发(是游戏开发,而非cocos开发,更不是web开发)看了都直饶头。

比如插件需要扩展修改原有inspector逻辑,cocos做不到。为什么做不到呢?因为用户写的inspector插件和cocos自带的inspector插件压根不在一个进程。用户是做不到,但是cocos本身做得到吗,做得到!只需要有清晰的架构就完全可做。为什么不做呢!别问,问就是不想做。可以自己实现一个笔刷在cocos场景里面绘制吗?不可以,通通不可以。架构就是如此不UGC。cocos希望所有的插件开发者熟悉cocos editor就像cocos editor开发者一样熟悉editor本身。当然引擎已经把大部分inspector放到了cocos engine / editor下开源出来了。但是由于上手成本实在太高,折腾收益完全不值得。

我只想说,距离一个合格的,更多人愿意开发插件的架构还是太远。为什么js可以在世界上拥有最多程序,npmjs 是世界上最大的库管理。别问,问就是js足够简单。所以,别问,问就是cocos editor plug插件开发实在是足够复杂。且 自由度 很低很低。

5赞

插件结构设计已经定下来了,要改也是 creator 4.0。 3.x 目前只能用这套机制。这个文档主要是让大家更方便开发 panel, 而编辑器的panel 就是个纯粹的 web 的东西,不谈 web 谈什么呢?

而且,请问什么地方是属于hack, 如果没有使用 第三方 UI 库, 其他一切流程,都是非常常规的 web 开发方式。

针对 第三方 的 UI 库需要 hack ,也说明了原因,那是我们用了web-component 导致。

会的人不看 看的人不懂 :joy:和新手一点关系都没有

2赞

官方正在向美好的方向探寻,支持一波 :kissing_cat:

感谢引擎组对插件系统的关注,大部分同意前面 @972579559 @544430497 @1226085293 大佬们说的。

这堆屎山还是抛弃吧,别浪费时间了,赶紧 4.0 重构插件系统吧。

编辑器不开源,插件系统还整成这样,不是我们想什么东西都要官方做,但是只有运行时开源,我们即使想无私贡献,即使愿意吃屎,也做不到啊。

6赞

插件还是换一个思路,不一定要用 web 那一套。开发者肯定是会 Cocos 这一套。你让开发者去弄 css,html,UI 学 2 套?
插件开发的基本流程应该是:
1、创建插件;
2、调用插件 API;
3、专注业务逻辑
4、上架;
官方让开发者去注意各种乱七八糟的配置。谁看得下去?不要用开发的思维去做产品啊。能实现和简单是 2 个概念。

说几个痛点:
1.打包构建的包没有工作流,直接出node_mudule文件夹,用户下载完提交到git全给过滤了,本来几M的包一下子变成几百M
2.代码加密混淆,上传商店前还要费力去做加密,要不然不敢发上去卖
3.ui调试很痛苦,改行代码每次都要重启编辑器,和场景交互的脚本,普通编辑(这里的vue界面可能只要重新构建脚本打开一下就可以,但是不确定哪些可以哪些不可以的情况下,大部分都是重启)因为不确定到底会不会生效的情况下,为了防止不生效,大概率会选择最保险的做法,重启,漫长的等待
4.消息调用没有封装,特别麻烦,希望可以直接通过代码的方式进行调用,还有很多的隐藏api,要不停的测试,测试过程又特别痛苦,而不是每个都要打开消息面板去查找,附上我的封装editor_warp.rar (8.1 KB)
5.消息支持有些地方可以,有些不行,比如场景内脚本,编辑器右键,资源操作,发消息的预期结果不确定
6.ui界面开发能够快速接入vue组件,(我理解应该是这次解决的问题)
7.目前功能和编辑器本身的契合度太低,很难扩展编辑器本身功能,比如要添加一些节点边框,相机预览常驻,下拉定制,批量操作等功能都特别费劲,只能做一些边角料的扩展,或者脱离编辑器相关功能

确实如此。但是目前架构就这样了,也只能屎山上做优化了,我每次开发插件都想跳楼,一个menu都要先配置,都不能动态增加。
如果想变成unity那样,估计要放弃web开发架构,direct ui才是方向。实在不行真的可以参考laya 3.0

希望简化一下,拜托了 :rofl:

能不能在编辑器里面直接拼插件界面 :grimacing: