Creator插件系统初探:我是怎么实现gif转anim动画的
这里首先感谢一些插件小王子,写这个插件的过程中,小王子解惑了很多问题。包括插件开发如何调试,creator的渲染进程和主进程以及编辑器场景问题等。
学习插件开发,首先是通过官网的插件开发文档:http://docs.cocos.com/creator/manual/zh/extension/。
但是由于文档有点简陋,所以很难实现像样的功能。而且缺少整体的知识体系说明,容易摸不清头脑。
所以在参考了小王子的插件开发教程之后,决定自己搞一款插件试试水,了解一下插件开发的知识体系。
现在大致讲一下我对Creator编辑器以及插件开发的理解。
Creator编辑器Electron实现的,Electron有主进程和渲染进程。
主进程 - Main Process
*作为程序的入口点。
*可以使用和系统对接的Electron API - 创建菜单等。
*负责创建和管理渲染进程以及各种应用程序事件。
渲染进程 - Renderer Process
*可以有多个,每个对应一个窗口,每个都是单独的进程。
*支持Node.js和DOM API,相当于一个浏览器页面。
*可以使用一部分Electron提供的API。
*所有我们看到的都是渲染进程。
插件开发的package.json指定的main.js入口程序是在主进程中被调用的。我们可以在package.json中定义一些菜单,然后main.js去监听这些菜单的点击事件去创建编辑器面板等操作。
"main-menu": {
"i18n:MAIN_MENU.package.title/i18n:gif-to-animation.title": {
"message": "gif-to-animation:open-view"
}
},
这里我在扩展菜单下定义了一个子菜单,对应的消息是"open-view"。用户点击菜单就会发送这个消息。然后在mian.js中监听这个’open-view’消息,触发打开面板操作。
messages: {
'open-view' (event) {
Editor.Panel.open('gif-to-animation.view');
},
},
我们也可以在package.json中自定义编辑器面板,同时指定他的index.js,每一个编辑器面板是一个渲染进程,我么可以像写网页一样去实现这个面板。
"panel": {
"main": "panel/index.js",
"type": "dockable",
"title": "Simple Panel",
"width": 400,
"height": 300
}
编辑器还有一个特殊的渲染进程,场景渲染进程,就是我们看到的场景编辑器界面。
我们可以定义一个场景脚本,在 package.json
里添加 scene-script
字段指定这个脚本。
{
"name": "my-plugin-name",
"scene-script": "scene-walker.js"
}
这个脚本会被场景渲染进程执行,然后我们就可以在脚本中用平时写游戏的方式去操作这个场景。什么cc.resources,node.x ,getChild,cc.find等引擎api都可以调用来操作。然后就可以用我们最熟悉的方式去写插件功能了。
怎么实现gif-to-animation插件
就gif转creator帧动画这个插件,要实现这个功能分三步。
1.要解析gif文件,把图片一帧一帧的存下来。
2.把这些图片资源导入到项目下,生成.meta文件,同时获取到图片的uuid。
3.根据这些uuid生成.anim动画文件。
解析gif文件
按照需求找轮子,首先找下有没有js版本的解析gif文件库。github上看看,果然有现成的轮子。只要移植就好了。轮子地址:https://github.com/buzzfeed/libgif-js。轮子本身是基于浏览器的。而编辑器渲染进程也支持浏览器环境。尝试移植,测试代码原封不动的拷贝,渲染进程index.js内是可以使用,下面是测试轮子的代码。
parseGif: function (gifFile, tmpDir, callback) {
const gifImg = document.createElement('img');
gifImg.setAttribute('rel:animated_src', URL.createObjectURL(gifFile));
gifImg.setAttribute('rel:auto_play', '0');
// Modified pictures must be added to the body
document.body.appendChild(gifImg);
// Construction example
const SuperGif = Editor.require('packages://gif-to-animation/utils/libgif.js');
var rub = new SuperGif({ gif: gifImg });
rub.load((gif) => {
let dalayTime = rub.get_delay();
var img_list = [];
var gif_name = gifFile.name.replace('.gif', '');
for (let i = 1; i <= rub.get_length(); i++) {
// Traversing through each frame of a GIF instance
rub.move_to(i);
// Converting each frame of canvas into a file object
let filename = gif_name + `-${i}` + '.png';
let fullFileName = path.join(tmpDir, filename);
this.convertCanvasToImage(rub.get_canvas(), fullFileName);
let item = {};
item.fullpath = fullFileName;
item.name = filename;
img_list.push(item);
}
callback(null, gif_name, dalayTime, img_list);
});
},
第一步就简单实现了。
把资源导入到项目
找了下官方文档,调用Editor.assetdb.import接口可以实现。官网代码:
//renderer process
Editor.assetdb.import( [
'/file/to/import/01.png',
'/file/to/import/02.png',
'/file/to/import/03.png',
], 'db://assets/foobar', callback);
这里吐槽一下,官网文档代码有问题,函数第三个参数是boolean值,第四个才是回调,可用代码是下面的。
Editor.assetdb.import(list, dest, true, function ( err, results) {
if (err) {
Editor.log(err);
}
self.makeAnimation(results, gif_name);
});
返回的results里有资源列表和对应的uuid。
根据这些uuid生成.anim动画文件
找了下官方文档,文档并没有提供动画编辑器相关的插件API。估计是目前没打算支持这块。但是没关系,由于我们只是试下帧动画,就是几个时间点去切换精灵的spriteFrame。这里新建一个.anim拖了几帧图片进去,看下生成的.anim文件。就是一个json文件,而且结构比较简单。
{
"__type__": "cc.AnimationClip",
"_name": "ani_test",
"_objFlags": 0,
"_native": "",
"_duration": 0.8,
"sample": 60,
"speed": 1,
"wrapMode": 2,
"curveData": {
"comps": {
"cc.Sprite": {
"spriteFrame": [
{
"frame": 0,
"value": {
"__uuid__": "9eebd21c-192a-4be6-8b4a-761b6d625649"
}
},
]
}
}
},
"events": []
}
spriteFrame字段的frame对应的是时间,value对应的是spriteFrame的uuid。
然后尝试去代码生成了一个json,放到编辑器中,ok可以使用。
//生成anim文件json数据
let animation = {};
animation['__type__'] = "cc.AnimationClip";
animation['_name'] = "ani_" + gif_name;
animation['_objFlags'] = 0;
animation['_native'] = "";
let frameCount = 3;
if (this._delayTime != 0) {
frameCount = parseInt(60 / (1 / (this._delayTime * 0.01)));
}
let frameTime = 1 / 60 * frameCount;
animation['_duration'] = spriteFrameUuids.length * frameTime; //动画时长
animation['sample'] = 60; //帧率
animation['speed'] = 1; //速度
animation['wrapMode'] = 2; //循环播放
animation['curveData'] = this.getFrameAnimationData(spriteFrameUuids, frameTime);
animation['events'] = [];
let str = JSON.stringify(animation, null, 4);
let anim_file_name = 'db://assets/resources/gif-to-animation/ani_' + gif_name + '.anim';
把写好的.anim文件导入编辑器。
Editor.assetdb.create(anim_file_name, str, function (err, results) {
results.forEach(function ( result ) {
// result.uuid
// result.parentUuid
// result.url
// result.path
// result.type
});
});
所有的功能都实现了,只要弄个界面就可以了。
编写编辑器面板界面
关于编辑器面板的编写,请先阅读官网文档:
编写面板界面:http://docs.cocos.com/creator/manual/zh/extension/writing-your-panel.html
掌握 UI Kit:http://docs.cocos.com/creator/manual/zh/extension/using-ui-kit.html
Editor.Panel.extend({
style: ``,
template: ``,
});
我们可以指定面板index.js的style和template。style属性可以指定.css。template指定.html来完成页面的ui布局等。
style: fs.readFileSync(Editor.url('packages://gif-to-animation/panel/index.css'), 'utf-8'),
template: fs.readFileSync(Editor.url('packages://gif-to-animation/panel/index.html'), 'utf-8'),
这里用到了一个类型的标签去选择gif文件。以及Creator插件内置的 ui-editbox 和ui-button等。
整个面板编写流程都是web前端的技术栈。我这里也只是初步接触,就不在赘述。
这样整个插件开发就完成了。
最终效果视频可以关注公众号查看。
插件下载地址:https://store.cocos.com/#/resources/detail/2573
虽然是收费的,但是也开源了,您也可以自己下载源码自己参考学习。当然如果支持一下作者那肯定万分感谢。
源码地址:https://gitee.com/li_wenlong/gif-to-animation.git
如果文章对您有一些帮助,那么欢迎您关注公众号’creator小玉米‘。公众号会不定期发送一些编程经验。