软磨硬泡 终于以不漂亮的方式解决了下。这里附上完整的代码;有兴趣的可以借鉴,同时把握遇见的问题能回答下更好。(这里是如何扩展自定义组件的inspector,官方文档太简单,没有说明之间的数据怎么通讯和存储)
首先我的自定义组件UIPrefab.ts。内容如下:
import { _decorator, Component, Node, UITransform, Scene } from ‘cc’;
const { ccclass, property } = _decorator;
@ccclass(‘UIItem’)
export class UIItem {
@property
public NodeName:string = “”
@property({type:Node})
public UINode:Node = null
@property
public ComponentName:string = “”
}
@ccclass(‘UIPrefab’)
export class UIPrefab extends Component {
@property({type:[UIItem]})
public nodeList:UIItem[] = Array()
@property({type:Node})
public myNode:Node = null
onLoad() {
for(let i = 0; i < this.nodeList.length; ++i) {
// this[this.nodeList[i].NodeName] = this.nodeList[i].UINode
console.log("UUUUUUUUUU:",this.nodeList[i].NodeName,this.nodeList[i].ComponentName,this.nodeList[i].UINode)
}
}
public addItem() {
let item = new UIItem()
this.nodeList.push(item)
}
public removeItem(index) {
let item = new UIItem()
this.nodeList.splice(index,1)
}
private findNodeByUuid(root:Node,uuid:string) {
if(root.uuid == uuid) {
return root
}
for(let i = 0; i < root.children.length; ++i) {
if(root.children[i].uuid == uuid) {
return root.children[i]
}else {
let e = this.findNodeByUuid(root.children[i],uuid)
if(e != null) {
return e
}
}
}
return null
}
public setItemNode(index:number,uuid:string) {
if(uuid != "") {
let node = this.findNodeByUuid(this.node.scene,uuid)
this.nodeList[index].UINode = node
}else{
this.nodeList[index].UINode = null
}
}
public setItemName(index:number,name:string) {
this.nodeList[index].NodeName = name
}
public setItemComName(index:number,comName:string) {
this.nodeList[index].ComponentName = comName
}
public getNodeComponents(uuid:string) :string[]{
let list:string[] = []
let e = this.findNodeByUuid(this.node.scene,uuid)
if(e != null) {
let coms = e.getComponents(Component)
for(let i = 0; i < coms.length; ++i) {
let name = coms[i].name
name = name.replace(coms[i].node.name,"")
name = name.replace('<',"")
name = name.replace('>',"")
list.push(name)
}
console.log("coms:",list)
}
return list
}
}
然后是通过编辑器创建的扩展,文档有说明如何创建这个扩展和使用,代码如下:
‘use strict’;
type Selector<> = { : Record<keyof $, any | null> }
export const template = `
<ui-label slot="label">节点列表</ui-label>
<ui-num-input slot="content" class="nodeNum"></ui-num-input>
<ui-button slot="content" type="icon" style="max-width:30px" class="addBtn">
<ui-icon value="add"></ui-icon>
</ui-button>
<ui-prop slot="content">
<ui-prop>
<ui-label slot="label">绑定名字</ui-label>
<ui-input slot="content" id="bindName"></ui-input>
</ui-prop>
<ui-prop>
<ui-label slot="label">节点对象</ui-label>
<ui-node slot="content" droppable="cc.Node" class="node" id="xxx"></ui-node>
</ui-prop>
<ui-prop>
<ui-label slot="label">绑定组件</ui-label>
<ui-select slot="content" id="ui-select" value="None">
</ui-select>
</ui-prop>
</ui-prop>
<ui-prop slot="label">
<ui-button type="icon" style="max-width:30px" id="delete">
<ui-icon value="del"></ui-icon>
</ui-button>
</ui-prop>
`;
export const $ = {
label:'.label',
test: '.test',
nodeNumInput: '.nodeNum',
listContent:".list",
element:".element",
addBtn:".addBtn"
};
interface UIItem {
NodeName:string;
Uuid:string;
ComponentName:string
}
type PanelThis = Selector<typeof $> & { dump: any };
export async function update(this: PanelThis, dump: any) {
// 进入引擎,启动游戏都会主动执行这个函数
// 缓存 dump 数据,请挂在 this 上,否则多开的时候可能出现问题
this.dump = dump;
// 将 dump 数据传递给帮忙提交数据的 prop 元素
// this.$.test.dump = dump.value.label;
// 更新负责输入和显示的 input 元素上的数据
// this.$.testInput.value = dump.value.label.value;
// this.$.label.value = dump.value.label.name;
this.$.element.style.display = "none";
this.$.nodeNumInput.value = dump.value.nodeList.value.length
while(this.$.listContent.firstChild) {
this.$.listContent.removeChild(this.$.listContent.firstChild)
}
// dump这是默认传过来的序列化的对象,我不知道怎么创新新的Node对象,我就自己转存一下
let nodeList = []
for(let i = 0; i < dump.value.nodeList.value.length; ++i) {
let elea = dump.value.nodeList.value[i]
var ele:UIItem = {
NodeName: elea.value.NodeName.value,
Uuid: elea.value.UINode.value.uuid,
ComponentName: elea.value.ComponentName.value
}
nodeList.push(ele)
}
dump.value.nodeList.value = nodeList
// 转存结束
for(let i = 0; i < dump.value.nodeList.value.length; ++i) {
let nodeUuid = dump.value.nodeList.value[i].Uuid
let cloneEle = this.$.element.cloneNode(true)
this.$.listContent.appendChild(cloneEle)
cloneEle.style.display = "block";
let nameEle = findNode(cloneEle,"bindName")
let nodeEle = findNode(cloneEle,"xxx")
let selectNode = findNode(cloneEle,"ui-select")
let delBtn = findNode(cloneEle,"delete")
nodeEle.addEventListener('change', async (event: any) => {
const nodeUuid = event.target.value;
Editor.Message.send("scene", "execute-component-method", { uuid: this.dump.value.uuid.value, name: "setItemNode",args:[i,nodeUuid] });
if(nodeUuid != "") {
let coms = await Editor.Message.request("scene", "execute-component-method",{ uuid: this.dump.value.uuid.value, name: "getNodeComponents",args:[nodeUuid] })
initOption(cloneEle,coms)
}else{
for(let i = selectNode.childNodes.length-1; i > 0 ; --i){
selectNode.removeChild(selectNode.childNodes[i])
}
dump.value.nodeList.value[i].ComponentName = "None"
Editor.Message.send("scene", "execute-component-method", { uuid: this.dump.value.uuid.value, name: "setItemComName",args:[i,"None"] });
}
});
nameEle.addEventListener('confirm', async (event: any) => {
const name = event.target.value;
dump.value.nodeList.value[i].NodeName = name
Editor.Message.send("scene", "execute-component-method", { uuid: this.dump.value.uuid.value, name: "setItemName",args:[i,name] });
});
selectNode.addEventListener('change', async (event: any) => {
const comName = event.target.value;
dump.value.nodeList.value[i].ComponentName = comName
Editor.Message.send("scene", "execute-component-method", { uuid: this.dump.value.uuid.value, name: "setItemComName",args:[i,comName] });
});
delBtn.addEventListener('confirm', async (event: any) => {
Editor.Message.send("scene", "execute-component-method", { uuid: this.dump.value.uuid.value, name: "removeItem",args:[i] });
dump.value.nodeList.value.splice(i,1)
this.$.nodeNumInput.value = dump.value.nodeList.value.length
this.$.listContent.removeChild(cloneEle)
});
let coms = await Editor.Message.request("scene", "execute-component-method",{ uuid: this.dump.value.uuid.value, name: "getNodeComponents",args:[nodeUuid] })
initOption(cloneEle,coms)
selectNode.value = dump.value.nodeList.value[i].ComponentName
nameEle.value = dump.value.nodeList.value[i].NodeName
nodeEle.value = nodeUuid
}
}
function findNode(ele:any,id:string):any {
for(let i = 0; i < ele.childNodes.length; ++i) {
if(ele.childNodes[i].id == id) {
return ele.childNodes[i]
}else{
if(ele.childNodes[i].childNodes.length > 0 ) {
let e = findNode(ele.childNodes[i],id)
if(e != null || e != undefined){
return e
}
}
}
}
}
function initOption(ele:any,ops:string[]) {
let eNode = findNode(ele,"ui-select")
if(eNode != null) {
while(eNode.firstChild) {
eNode.removeChild(eNode.firstChild)
}
let op = document.createElement('option')
op.value = "None"
op.innerText = "None"
eNode.appendChild(op)
for(let i = 0; i < ops.length; ++i) {
let op = document.createElement('option')
op.value = ops[i]
op.innerText = ops[i]
eNode.appendChild(op)
}
eNode.value = "None"
}
}
export function ready(this: PanelThis) {
// 监听 input 上的提交事件,当 input 提交数据的时候,更新 dump 数据,并使用 prop 发送 change-dump 事件
let p = this
this.$.addBtn.addEventListener('confirm', async () => {
console.log(this.dump)
var ele:UIItem = {
NodeName: "",
Uuid: "",
ComponentName: ""
}
this.dump.value.nodeList.value.push(ele)
console.log(this.dump.value.nodeList.value.length);
this.$.test.dispatch('change-dump');
this.$.nodeNumInput.value = this.dump.value.nodeList.value.length
let cloneEle = this.$.element.cloneNode(true)
this.$.listContent.appendChild(cloneEle)
cloneEle.style.display = "block";
let nodeEle = findNode(cloneEle,"xxx")
let selectNode = findNode(cloneEle,"ui-select")
let nameEle = findNode(cloneEle,"bindName")
let delBtn = findNode(cloneEle,"delete")
let i= this.dump.value.nodeList.value.length-1
nodeEle.addEventListener('change', async (event: any) => {
const nodeUuid = event.target.value;
ele.Uuid = nodeUuid
Editor.Message.send("scene", "execute-component-method", { uuid: this.dump.value.uuid.value, name: "setItemNode",args:[i,nodeUuid] });
if(nodeUuid != "") {
let coms = await Editor.Message.request("scene", "execute-component-method",{ uuid: this.dump.value.uuid.value, name: "getNodeComponents",args:[nodeUuid] })
initOption(cloneEle,coms)
}
});
nameEle.addEventListener('confirm', async (event: any) => {
const name = event.target.value;
ele.NodeName = name
const nodeUuid = ele.Uuid;
Editor.Message.send("scene", "execute-component-method", { uuid: this.dump.value.uuid.value, name: "setItemName",args:[i,name] });
});
selectNode.addEventListener('change', async (event: any) => {
const comName = event.target.value;
ele.ComponentName = comName
const nodeUuid = ele.Uuid;
Editor.Message.send("scene", "execute-component-method", { uuid: this.dump.value.uuid.value, name: "setItemComName",args:[i,comName] });
if(nodeUuid == "") {
let coms = await Editor.Message.request("scene", "execute-component-method",{ uuid: this.dump.value.uuid.value, name: "getNodeComponents",args:[nodeUuid] })
initOption(cloneEle,coms)
}
});
delBtn.addEventListener('confirm', async (event: any) => {
Editor.Message.send("scene", "execute-component-method", { uuid: this.dump.value.uuid.value, name: "removeItem",args:[i] });
this.dump.value.nodeList.value.splice(i,1)
this.$.nodeNumInput.value = this.dump.value.nodeList.value.length
this.$.listContent.removeChild(cloneEle)
});
// 通过此方法跟 组件对象进行通讯 坑爹啊
Editor.Message.send("scene","execute-component-method",{uuid:this.dump.value.uuid.value as string,name :"addItem"})
});
}
这里面就涉及到他们之间如何传递数据。