ShaderGallery v3 插件集成分享 && 提问

ShaderGallery Plugin Integration / 插件集成

About / 关于

ShaderGallery 是从 ShaderEditor 独立出来的插件:

其最主要的特点是:

  1. 功能简化,和 ShaderEditor 相比,能够让任何使用这都轻松上手使用
  2. 所见即所得,为自己的游戏,快速的浏览,挑选,使用 Shader 特效

ShaderGallery 旨在帮助对 Shader 并没有很深入认识的朋友,在使用 Creator 进行游戏制作时,可以像 购物 一样,自由 挑选,试用特效,最终找到最适合自己游戏的特效。

Tips / 提示

下文中所有的内容,基于 Creator v3.1.1,注意不同版本,可能会有些许差异。

这篇文章,其实只是为了提及个问题,不过我想写的丰富一点,应该更能引擎官方发主要,好得到解答 :rofl:

Shortcomings / 缺点

虽然上面的简介,让人觉得插件功能十分强大,但是实际上,现在插件还是有一些明显的不足之处。

  1. 插件的 网页版,独立于 Creator 编辑器,对于 ShaderEditor ,独立于 Creator 进行 Shader 的制作和编辑,还是很有用的,但是 ShaderGallery 更主要的还是要结合 Creator 编辑器使用,用户对于网页版的需求相对较低

  2. 除了 网页版ShaderGallery 也有 插件版 ,但是目前为止 ( v1.3.0 ) ,插件版Creator 的交互度很低,低到插件版仅仅只是一个 Launcher / 启动器 而已

  3. ShaderGallery 在让用户挑选特的时,提供了一些内置的 静态图片骨骼动画 用于预览特效,同时也支持让用户载入自己的美术资源,进行特效的预览,但是部分用户提出:

    单独的美术资源预览,脱离游戏场景,无法整体的感知特效配合美术资源,在最终游戏中能够达到的效果

Deep Integration / 深度集成

上面的缺点,想要一一解决,一个最基础的,最重要的点,就是:

大幅提高 ShaderGallery 插件版和 Creator 编辑器的集成度

Core / 核心

ShaderGallery 使用的开发技术不同于商店中的其他插件,更确切的说,使用的不是官方所推荐的 主流 做法。

[就问你6不6] cocos2dx 入侵 Creator 插件系统 - 0
[就问你6不6] cocos2dx 入侵 Creator 插件系统 - 1
[就问你6不6] cocos2dx 入侵 Creator 插件系统 - 2
[就问你6不6] cocos2dx 入侵 Creator 插件系统 - 3
[就问你6不6] cocos2dx 入侵 Creator 插件系统 - 4
[就问你6不6] creator 入侵 Creator 插件系统 - 5
[很快啊-就问你6不6] cocos2d-x 入侵 Creator 插件系统 - 6

虽然,这种做法,在自己看来有以下的几个优点:

  1. 只要会 Creator v1/2 ,即使不会 html / css / vue / node.js / ... 也可以开发进行插件,因为插件直接就是用 Creator 制作而成
  2. 开发出的插件,除了可以作为插件在 Creator 中运行(这是理所当然的), 还可以脱离引擎,直接运行在网页,甚至作为原生的 App 运行 (当然这需要一点点适配,但是真的只是一点点,不是亿点点)
  3. Creator 插件系统 所用到的一些技术没有直接的依赖,如 vue 或是 electron 之类,几乎不会因为 Creator 引擎升级之类的,出现奇怪的问题,换句话说,就是对 Creator 各版本的兼容性比较友好

其实这种做法的核心原理,就一句话:

Creator 制作发布的 web 项目,作为插件,嵌入到插件系统的面板中。

就好比,你的 Creator 编辑器里,有个插件,插件肚子里,又怀了个 Creator 项目。

当然,实际要做到这样的效果,还是要有不少骚操作的,特别是要在不改一行引擎代码的前提下,实现这个效果。

这种做法,有优点,缺点自然也是有的,其中最重要的一点就是:

因为做法的 非主流,在插件模式下,和 Creator 编辑器的集成还是有水土不服的情况,并且很难解决。

Dock vs Float / 停靠 vs 浮动

目前 ShaderGalleryShaderEditor 一样,在插件模式下,仅仅支持 Float 模式,就是只能够 以独立的窗口模式运行,在 dock / 停靠 模式下,无法正常的加载运行(如果显示器够大,甚至有双屏的话,float 也没啥问题,但如果是笔记本的话,就显得空间不够用了 )。

造成这个问题的原因,就是上面提到过的,插件的实现方式,并不是那种主流的方式。

之前也咨询过官方人员,得到的回复,也是:

插件在 float 模式下,和编辑器场景处于不同进程,不会有什么问题,但是 dock 模式下,插件会与场景编辑器的环境发生冲突,因此 不建议在场景进程中去使用场景,这样会出现未知情况,破坏场景编辑器的环境

在实际的尝试后,也确实如此,插件无法正常加载使用,虽然总觉得还差那么一点就可以实现,但是就是没有办法确实的去解决这个问题,很可惜。

碰巧最近开始在 Creator v3.x 中尝试插件的集成优化,突然想到,之前的问题是在 v2.x 版本中提出的,v3.x 的插件系统设计和实现都有别于 v3.x ,那么会不会这个问题在 v3.x 中能够解决呢。

于是简单的进行了尝试,虽然还是失败,无法加载,但这一次却出现了不同于之前的,比较可以解决的报错,在一番折腾之后,终于第一次,在 dock 模式下,看到了加载后的插件,并且也可以和 网页版插件的 float 模式 一样,正常的使用。

Mode Switch / 模式切换

貌似 dock 的问题,被比较好的解决了,但是在后续实际的测试时,还是发现了一些问题。

Startup Dock / 启动即停靠

Float to Dock / 浮动切停靠

float_to_dock

Dock to Float / 停靠切浮动

以上三种用例,在测试的时候,都没有发现特别的问题。

Dock to Dock / 停靠切停靠

这种情况下,出现了和之前 v2.x 比较类似的报错,后来进一步测试发现,以上三种情况,第一次切换是正常的,但是如果再接着进行切换,同样也会出现报错 – 也就是说,第一次的加载插件是没问题的,但是再次切换模式的时候,就会出错。

在经过一些调查,怀疑是在切换模式的时候,内嵌插件包含的引擎代码重复加载出现的错误,于是想要通过控制这个部分来尝试解决,但是在查看了文档和论坛后,没有找到关于这一块的说明,因此这里想要请教下:

Question.01

v3.x 中,插件在 float / dock 模式之间,相互进行切换,有什么全局消息之类的通知,或是在插件被加载的时候,可以判断的方式吗?

Resize / 缩放

在停靠模式下,有时用户会需要调节插件部分窗体的大小,不同于 float / 模式 下,插件时一个独立的 Panel,窗体大小发生变化的时候,内部插件,也会像游戏适配一样,自动的进行适配缩放,dock / 停靠 模式下,父容器的缩放,并没有带动内部的插件进行缩放。

不过这一点,在扒源码后,使用了插件系统提供的 resize 函数,自己进行手动触发缩放,得到了解决:

exports.listeners = {
	resize() {
		// ......
		cc.view._resize();
	}
};

原以为到这里就解决了缩放的问题,但是在使用中,又意外的发现,在 dock / 停靠 模式下,当插件处于非激活状态,比如切换到了 Console 面板,查看日志的时候,调整窗体大小,会出现报错的情况。

好在,这也能够通过插件系统提供的消息函数来解决:

exports.listeners = {
	hide() {
    },
	show() {
	}
};

就像前面提到的一样,出现这些问题的原因,是插件的实现选择了非主流的做法,如果是正统的,按照官方的规范开发的插件,自然是不会有这样的问题的,正所谓有得必有失。

Interaction / 交互

在一定程度上解决了 dock 问题后,插件开始真正的有了插件的样子,但是这还远远不够,接下来需要做的,就是 插件Creator 编辑器 的深入交互了。

Plugin System / 插件系统

在开始交互前,需要明确一下插件系统的一些概念:

对于 ShaderGallery 是这个样子的:

Creator Editor // Creator 编辑器
	┣━ Scene // Creator 中的 场景编辑器
	┣━ Assets // Creator 中的 资源试图部分
	┣━ Hierarchy // Creator 中的 节点数部分
	┣━ ...... // Creator 编辑器中的各个面板部分
	┣━ Gallery // 用户自定移动插件主体
		┣━ main // 自定义插件,入口程序,运行在 主进程,被 Creator 加载,并调用其 load 函数
			┣━ panel // 自定义插件,面板,可以有多个,运行在 渲染进程,主要用于显示插件
				┣━ SSRGallery // Gallery 插件,Creator 发布的 `web` 项目,被加载到 panel 中显示

看完上面的图,接着要实现的一些,所谓的交互,其实也就是 从某一个起点向某一个目标发送消息,随便举两个例子:

  1. 选中场景编辑器中的任意节点,在插件中,输出被选节点的信息
  2. 插件中点击某个按钮,在 资源管理器 中新建一个文件

对于 ShaderGallery 而言,这两个需求其实就是:

  1. Scene >>> SSRGallery
  2. SSRGallery >>> Assets

当然,实际的实现,可能就没那么容易了。

不过在实际学习实践后发现,v3.x 中的插件系统比 v2.x 还是成熟,先进了许多。

最基础的,也是最核心的一点,那就是 Message / 消息,万物皆用消息来通信,串联。

消息系统 · Cocos Creator

Creator to Plugin

首先是从 Creator 一端到 插件 的交互。

这样的需求,通常是需要在 Creator 中进行某个操作 时候触发的,就像上面例子里的,场景中选择了某个节点对象

就如文档中写的,这类消息通常属于 broadcast / 广播消息,只要接收这样的广播就行了,比如一个 选择了某个节点 的广播。

那么问题就变成了,这个广播到底是什么呢。

Message DevTools / 消息调试

v2.x 中,比较痛苦的,就是去 猜 (打印) 这样的消息,或者就是靠前人的总结:

《creator插件开发常用API命令列表》备忘录

但这一次,v3.x 中提供了强大的 Message DevTools 工具,想知道是什么消息,再也不用去猜,实际操作一遍,看一眼就行了

可以看到,实际操作以后,触发的一些消息都会被显示出来,这里可以明显的看到,广播的消息是 selection:select

知道了消息,按照文档操作,就可以顺利的拿到消息通知了:

// package.json
{
    "contributions": {
        "messages": {
            "selection:select": {
                "methods": [
                    "selectionSelect"
                ]
            }
        }
    }
}

// main.ts
exports.methods = {
    selectionSelect(node, uuid) {
        console.log("selectionSelect called");
        console.log(node);
        console.log(uuid);
      	//  output: node f31kvgxshJmIxJnCUl4kD0
    }
};

Tips / 提示

消息的接收,可以在 主进程 / main 进行,也可以在某个 渲染进程 / panel 中进行,只要根据自己的插件业务逻辑需要,具体可以参考文档:

与其他面板的通信 · Cocos Creator

Data Send/ 数据发送

当我们得到想要广播消息事件回调后,还远远不够,回顾上面的图片,此时我们还停留在 main / 主进程 的位置。

我们要做的,是把 发生了么,涉及哪些数据 这样的信息,传递到我们的插件中。

v3.x 插件系统,主动发送某条消息到某个功能(扩展),需要使用 Send / 发送 接口:

// package.json
{
    "contributions": {
        "messages": {
            "sceneNodeSelect": {
                "methods": [
                    "default.sceneNodeSelect"
                ]
            }
        }
    }
}

// main.ts
Editor.Message.send('ssr-shaderfx-gallery', 'sceneNodeSelect', uuid);

// ssr-shaderfx-gallery/panels/index.ts
exports.methods = {
	sceneNodeSelect(uuid) {
		console.log("sceneNodeSelect called");
		console.log(uuid);
	}
};

像这样,我们就可以将数据在不同进程之间相互传递了。

Scene Script / 场景脚本

虽然自己的插件并没有使用,但是在研究的过程中也尝试了一下。

场景脚本在 v2.x 中开始就一直存在:

场景脚本 · Cocos Creator

最大的亮点就是:

简单来说,在这里,你就拥有了 cc 的环境,就可以像平时写代码一样地对节点进行操作,而不需要引用编辑器那一套 API

比如说可以:

// v2.x 语法例子
node.removeFromParent(true); // 从场景中移除
node.addComponent(cc.Label); // 给节点加一个组件

还是非常方便的。

你要做的,只是在 Creator 编辑器发生某些你感兴趣的变化时,监听事件,并将数据传递到这个场景脚本中,即可。

传递的方式,也很简单:

Editor.Message.request('scene', 'execute-scene-script', {
	  name: string; 	// 要调用的场景脚本所处包名
    method: string;	// 要调用的场景脚本中的函数名
    args: any[];		// 要调用的场景脚本时传递的参数
});

Tips / 提示

但是这里有一个点,要注意,最好不要在场景脚本中,将原生对象,比如一个 cc.Node 对象,再次传输给其他的,比如 panels / 渲染进程


实际尝试后,编辑器也确实就卡死了,无法继续使用,只能强制重启。

Data Request / 数据请求

到这里,我们其实可以看到,数据传输的时候,插件系统并不推荐(或者说是禁止)用传输原生对象这种方式,而实际上,也是如此,传输的大多是比较简单的 json 对象,其中见得最多的,就是 uuid

因为有了 uuid 我们就可以按需,去请求我们想要的对象的信息,方法也是很简单,对应前面的 send / 发送 接口,这里用的是 request / 请求 接口。

// ts 例子
const info = await Editor.Message.request('scene', 'query-node', uuid);
// js 例子
Editor.Message.request("scene", 'query-node', uuid).then(info => {}).catch(err => {});

请求获取到的结果,基本都是 json ,至于具体内容,就要通过打印或者断点调试来查看了。

这里再举个 ShaderGallery 的实际例子

在插件中,使用插件界面中控件 适时调整 Shader 特效的同时,需要让场景编辑器中被选中,并且启用同步特效的节点,同样可以实时刷新预览。

这里还是老样子,先通过消息调试工具,来看一下修改 effect 中的参数时,大致发生了什么事:

可以看到,这里主要牵涉到了两个接口 preview-material 以及 material-inspector:change-dump

接着就是只要依葫芦画瓢,照着样子,发送对应的消息请求就可以了。

实际操作后,基本没啥问题,但是期间碰到了一些小问题,这里也提出一下:

Question.02

视频中可以看到,在 Message DevTools 中,当参数过程时,在界面中显示时,没有提供垂直滚动功能,导致无法预览所有数据细节。这个是不是有问题?

Question.03

一次数值属性的修改,如 TilingOffsetx 属性,从 1 改为 2

Message DevTools 中,可以看到如下的消息序列:

renderer request preview-material

renderer request preview-material

renderer broadcast material-inspector:change-dump

这里的,preview-material ,有在 material-inspector:change-dump 出现前,会有 3 次,甚至 4 次,且这 n 次发送的数据参数,完全一样。

就实际通过代码控制属性的修改来看,1preview-material + material-inspector:change-dump 就可以实现效果。

是不是这里插件系统的实现有些问题?

Question.04

通过 preview-material + material-inspector:change-dump 组合,可以实现修改属性 (场景编辑器中可以看到效果),但是 属性面板 的对应属性数值,却不会改变 (这个其实可以理解)。

当最后调用 save-asset 以后,属性面板 的对应属性数值会刷新改变。

这里主要想问下,有没有什么方法,能够通过消息,控制到这个属性面板里的对应值,也可以实时变化 (就像手动在改变属性值一样) ?

Question.05

ShaderGallery 不光支持对 3d 对象的实时 Shader 特效预览,还做了对各种 2d 对象的支持。
在实现过程中,发现一个问题,Spine 动画 对象,在通过代码方式 (或是直接拖放材质更新的方式), material + effect 后,会出现奇怪的问题

  1. 代码确实生效,运行后可以看到,材质已经更换成功
  2. 在 Creator 场景编辑中,拖放材质后,Spine 节点内容会消失不可见
  3. 这时候,随便点个比如 Debug Slot 或是 Debug Mesh Spine 动画就会恢复正常可见,并且可以看到更新的材质也已经确实生效了
  4. 然后,这时候如果调节一下材质的某个 Uniform 属性,场景编辑器中动画又会不可见,这时候再一次刷一下 Debug Slot 之类的,就又会恢复正常
  5. 顺便一提,Dragon 龙骨动画,更换材质或是调节参数后,场景编辑器中不会消失不可见,但是材质效果却看不到,只有实际运行才能看到
  6. 重启 Creator 编辑器后,Spine Dragon 都可以在 场景编辑器 中看到带有材质的效果,但是同样的,只要再重新变换材质,或是调节参数,前者立刻原地消失,后者就是保持没有效果
    想要问下,这个是不是编辑器的问题 ?

Conclusion / 总结

得益于 v3.x 的插件系统设计,现在的插件来发比起 v2.x` 其实要方便许多,其实只要摸到了门道以后,什么都能做出来,只要:

  1. 确定一下想要什么功能
  2. 自己在 Cretor 里操作一下
  3. 记得打开 Message DevTools 看一下 Creator 怎么做的
  4. 自己也照着发消息做一下,再加入自己需要的业务逻辑
  5. 基本你就有了你想要的小功能
  6. 同类的小功能汇集一下,简单的插件就出来了

ShaderEditorShaderGallery 都会继续更新,接下来的版本都会朝着 和 Creaotr 编辑器的重度集成 方向进行。

因为一些比较麻烦的问题得到了一定程度的解决,接下来,应该能够做到,在 ShaderEditorShaderGallery 中的操作,实时的反馈到 Creator 编辑器 中,做到更加强大的支持 WYSIWYG / 所见即所得 的插件,让用户更加方便的使用。


Creator v3.x 中 2D 对象是会预览,编辑(开发中)
https://www.bilibili.com/video/BV1H44y187Ro/

Creator v3.x 中 3D 对象是会预览,编辑(开发中)
https://www.bilibili.com/video/BV1RM4y137nZ/


3赞

文中的一些问题,希望官方人员可以抽空简单答复下,给个方向也行 (看在这么长的文章的份上 ) ,感谢 ~~~

1赞

Mark!

你这问的问题,估计整个论坛里面能回答的没几个人。

期待官方大大的解答,确实很疑惑,就是3.0版本就不行,2D的还好

1赞

一步两步,跟随大佬的脚步