CocosCreator编辑器扩展4-⾃⼰实现⼀个简单的⼩插件
由于前段时间⽐较忙所以拖更了,这⾥也⼗分抱歉,加上我这个⼈平时不经常写博客,所以会慢⼀点,以后这种连续的博客我不会拖更了。那么进⼊正题,今天我们实现⼀个简单的⼩插件,其实呢这个⼩插件也没什么卵⽤,只是可以给更多需要的⼈提供⼀个思路,同样对我来说也是⼀个笔记,如果各位朋友感兴趣的可以继续往下看。
⾸先为什么要写编辑器呢?我个⼈的理解不过于就是⽅便我们在平时开发,写⼀些和引擎交互的东西,可以实现⼀键搞定重复的操作,⽐如说你要对⼀个⽂件夹下的所有预制体都添加⼀个脚本,那么我们⼀个⼀个添加也可以,但肯定会对于这个重复的操作感到⽆聊同样也会浪费开发时间,要是⼀键就能搞定,那会多么⽅便是吧,但是转念⼀想可能写的时间还不如⾃⼰⼀个⼀个拖拽了,所以这个问题的取舍当然也看项⽬⼤⼩,进度等等情况来看,到⼀个合适的解决办法才是最好的解决办法。然后开始我们今天的这个⼩插件。
⾸先说下这个写这个插件的意义,也就是需求,假设需求是这样的:我想弄⼀个简单的⽐如关卡的存储管理,把场景下的物体在我搭完之后,⼀键可以把场景的⾥的物体的位置保存到⼀个json的配置表中,然后我在加载某⼀个关卡的时候,根据表中的数据信息把场景还原回来,当然有⼈也会问那直接做个预⽀体不好么?当然也可以,所以也根据项⽬来看。我突然想到这个也可以⽤来做⼀下单机游戏存档是吧。好了,
我们给⾃⼰的需求定好了,那么我们⾃⼰看下要怎么去实现这个插件,也就是逻辑。⾸先分为⼏个部分,1.也就是我们的保存的数据位置,我们需要⼀个固定的⽂件夹来存放每⼀个关卡的配置数据,对资源⽂件的操作部分2.我们需要⼀个⾯板也就是我们场景搭建完成后需要⼀键保存的⼀个⾯板,⽽⾯板中有什么功能呢,要有⼀个⼀键⽣成的存储路径,还要有根据路径⽣成的按钮,以及我删除这个配置数据的按钮,还可能有刷新这个资源下配置表的数据的按钮,当然最好还要有⼀个换下⼀关时,直接⼀键把之前搭建好的场景⼀键清空,换下⼀关的按钮,⾄少我觉得这些都要有吧,3.我们需要把场景的物体怎么通过⾯板上⼀键存到json配置表中呢,这⾥涉及到场景和⾯板的进程交互。所以总结下来这三点,那么按照逻辑我们⼀步⼀步来吧,开始前还是先看张效果图吧:
其实也并不是什么完整的关卡配置数据表,只是我取的⼀个名字因为不知道叫什么,看下操作流程:我在root节点下⽐如搭建好了⼀个关卡,我想把信息保存起来,点击tool下的按钮会弹出这个⾯板,如果路径不想改的话就⽤默认的,因为到时候还原数据的时候也要load加载,所以最好是在resources或者它的⼦⽂件夹下,点击⽣成配置表按钮会⼀键把root下⾯所有的⼦节点的信息保存到这个配置表中也就是level1那个json⾥,⾄于想保存什么信息可以⾃⼰在代码中设定,因为我之前⽣成了,所以在⽣成就是覆盖之前的⽂件了信息了,有的时候可能⽣成的数据在引擎没有没有及时刷新,点下刷新就好了,如果这⼀关不想要了,也可点击删除,完成⼀关后点击重置,会把root场景下的节点全部删除,好进⾏下⼀关配置。
接下来我们在⼯程的根⽬录下的packages⽂件夹下建⼀个名为level-configuration的⽂件夹,也可以⾃⼰换成⾃⼰想要的名字,我以我的举例⼦。⾄于为什么在packages下操作我前⾯博客有说。⾥⾯的⽂件结构如下:
main.js是这个插件的⼊⼝,panel下index.js是⾯板的代码,scene-obtain.js是操作场景的脚本,那package.json就不⽤说了,path暂时先没⽤到。看下⼯程⾥:
先看下package.json⾥的配置内容吧:
{
"name": "level-configuration",
"version": "0.0.1",
"description": "关卡配置表⽣成⼯具",
"author": "tm",
"main": "main.js",
"scene-script": "scene-obtain.js",
"main-menu": {
"Tool/LevelConfiguration": {
"message": "level-configuration:open-panel"
}
},
"panel": {
"main": "panel/index.js",
"type": "dockable",
"title": "LevelConfiguration",
"width": 400,
"height": 300
}
}
接下来是main.js⾥的内容,引擎启动后如果没有resources⽂件夹会⾃动创建⼀个⽂件夹,后⾯是⼀些对⽂件的操作在主进程中操作:
'use strict';
let fs = require('fs');
let path = require('path');
//默认关卡路径
let defaultLevelPath='assets/resources/';
load(){
this.init();
},
unload(){
Editor.log('卸载执⾏');
},
//初始化
init(){
},
//创建初始⽂件夹
//创建初始⽂件夹
createDirectory(){
istsSync(path.join(Editor.Project.path, defaultLevelPath))){
Editor.log('resources exists!');
}else {
// 插件加载后在项⽬根⽬录⾃动创建指定⽂件夹
fs.mkdirSync(path.join(Editor.Project.path, defaultLevelPath));
Editor.success('resources created!');
}
},
//创建关卡⽂件
createLevelFile(filePath){
let levelPath=filePath+".json";
istsSync(path.join(Editor.Project.path, levelPath))){
//覆盖源⽂件
}else {
//新建⽂件
}
},
//覆盖源⽂件
coverTargetFile(filePath){
Editor.Scene.callSceneScript('level-configuration', 'get-scene-info', function (err, json) {            Editor.assetdb.saveExists( 'db://'+filePath, json, function ( err, meta ) {
if(err){
Editor.log("覆盖⽂件失败");
return;
}
Editor.log("覆盖⽂件成功");
});
});
},
//创建⼀个新的⽂件
createNewFile(filePath){
Editor.Scene.callSceneScript('level-configuration', 'get-scene-info', function (err, json) {            ate( 'db://'+filePath, json, function ( err, results ) {
if(err){
Editor.log("创建⽂件失败");
return;
}
Editor.log("创建⽂件成功");
});
});
},
//删除⽂件
deleteLevelFile(filePath){
let levelPath=filePath+".json";
istsSync(path.join(Editor.Project.path, levelPath))){
Editor.assetdb.delete(['db://'+levelPath], function (err, results) {
results.forEach(function (result) {
if(err){
Editor.log("删除⽂件失败");
return;
}
Editor.log("删除⽂件成功");
});
});
}else {
Editor.log("没有可删除的⽂件");
}
},
messages: {
//打开⾯板
'open-panel'() {
Editor.Panel.open('level-configuration');
Editor.Panel.open('level-configuration');
},
//保存按钮点击
'save-click'(event, ...args){
const self=this;
},
//设置路径
'set-path'(event,...args){
if(args[0] && args!=''){
defaultLevelPath=args[0];
}
},
/
/⾯板加载完成
'panel-load-finish'(evnet,...args){
Editor.Ipc.sendToPanel('level-configuration','setDefaultPath', defaultLevelPath);
},
//删除按钮点击
'delete-click'(event,...args){
this.deleteLevelFile(args[0]);
}
},
}
然后看下⾯板index.js的代码,⽆⾮就是界⾯以及那⼏个按钮绑定的⽅法,和主进程的通信
let defaultLevelPath='assets/resources/';
let levelNameHeader="level_";
let levelName="";
d({
style: `
:host { margin: 5px; }
h2 { color: #f90; }
.bottom {
height: 30px;
}
`,
template: `
<h2 >关卡配置表⽣成器</h2>
<hr />
<div>
FilePath: <span id="label"></span>
<ui-input value="path" id="inputPath"}></ui-input>
</div>
<hr />
<div>
LevelName: <span id="levelLabel"></span>
<ui-num-input step=1 min=1 id="changeLevel"></ui-num-input>
</div>
<hr />
<div >
<ui-button id="btn" class="green">⽣成配置表</ui-button>
<ui-button id="deleteBtn" class="red">删除配置表</ui-button>
<ui-button id="updateBtn" class="blue">刷新资源</ui-button>
<ui-button id="resetBtn">重置场景</ui-button>
</div>
`,
$: {
//保存按钮
btn: '#btn',
//固定路径前label
代码编辑器怎么下载
label: '#label',
label: '#label',
//关卡路径前label
levelLabel:'#levelLabel',
//固定路径
inputPath:'#inputPath',
//更改关卡
changeLevel:'#changeLevel',
//删除按钮
deleteBtn:'#deleteBtn',
//刷新按钮
updateBtn:'#updateBtn',
//重置
resetBtn:'#resetBtn'
},
ready () {
Editor.Ipc.sendToMain('level-configuration:panel-load-finish');
this.init();
this.saveBtnClick();
this.setLevelConfigurationDefaultPath();
this.changeLevelEvent();
this.deleteBtnClick();
this.updateBtnClick();
},
init(){
this.$label.innerText = '(默认⽂件路径)';
this.$levelLabel.innerText='(关卡名称)'+levelNameHeader;
this.$changeLevel.value=1;
levelName=levelNameHeader+this.$changeLevel.value;
},
/
/更改关卡事件
changeLevelEvent(){
this.$changeLevel.addEventListener('change',()=>{
levelName=levelNameHeader+this.$changeLevel.value;
})
},
//重置场景按钮点击
resetBtnClick(){
this.$resetBtn.addEventListener('confirm',()=>{
Editor.Scene.callSceneScript('level-configuration', 'reset-scene', function (err, res) {            });
})
},
//刷新按钮点击事件
updateBtnClick(){
this.$updateBtn.addEventListener('confirm',()=>{
fresh('db://'+defaultLevelPath, function (err, results) {
if(err){
Editor.log("刷新⽂件⽬录失败")
return;
}
Editor.log("刷新⽂件⽬录成功")
});
})
},
//删除按钮点击事件
deleteBtnClick(){
this.$deleteBtn.addEventListener('confirm',()=>{
Editor.Ipc.sendToMain('level-configuration:delete-click',defaultLevelPath+levelName);        })
},
//保存按钮点击事件
saveBtnClick(){
this.$btn.addEventListener('confirm', () => {