creator3.x Inspector 扩展求助

目前只想在Component 的Inspector 上加一个按钮实现一下功能。
由于编辑器闭源不知道如何渲染Component自带属性,有大佬能帮助一下吗?
‘use strict’;

import { methods } from “./main”;

type Selector<> = { : Record<keyof $, any | null> }

export const template = `

\n

<ui-label>{{prop.name}}</ui-label>

\n

xxx

`;

//

// Auto Bind

export const $ = {

test: '.test',

btn: '.btn',

};

export function update(this: Selector<typeof $> & typeof methods, dump: any) {

console.log("[comp-label] dump:", dump)

console.log("[comp-label] frist:", this.value)

// 使用 ui-porp 自动渲染,设置 prop 的 type 为 dump

// render 传入一个 dump 数据,能够自动渲染出对应的界面

// 自动渲染的界面修改后,能够自动提交数据

// this.$.test.render(dump.value.label);

// this.$.test.render(dump.value.label2);

// this.$.test.render(dump.value.label3);

}

export function ready(this: Selector<typeof $> & typeof methods) {

this.$.btn.addEventListener('confirm', () => {

    console.log("[comp-label] ready:", this.$.test)

})

}

image 类似这样?

如果只是想加按钮功能,你可以试试我的插件 dylbutton
下载链接: Cocos Store
直接在你的方法上添加一个装饰器 @dylbutton 不过这个按钮是在另一个面板上显示(其实我也想把它直接移在 属性检查器 上, 但现在看到的是,这些自定义渲染的脚本都要在json注册,好像没法通用, 所以我才另外加个面板)

你还可以写成 @dylbutton(“f1”) 这样就可以给这个按钮添加快捷键了 (快捷键是触发场景上挂载的脚本, 不挂就不会触发, 挂载多份就触发多次)

其实这个插件的主要功能是添加全局按钮, 不需要的话,你可以自己删了,只留下这个装饰器功能. 我就不多介绍了,需要的话,你可以去看看

:smiley:

嗯 只是在原有Component 属性之后多加一个按钮

自定义组件,想自己实现Inspector的显示,扩展如何写呢?文档给的太简单,没有如何进行扩展脚本和游戏中的组件对象进行数据通信的列子吗?

软磨硬泡 终于以不漂亮的方式解决了下。这里附上完整的代码;有兴趣的可以借鉴,同时把握遇见的问题能回答下更好。(这里是如何扩展自定义组件的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"})

   

});

}

这里面就涉及到他们之间如何传递数据。

1赞

研究一下你的代码

其实,如果不在乎按钮的样式 ,可以直接用这样用image

1赞

:rofl: 这样的话每个脚本都要加了

怎么做的?可以分享下嘛 :joy:

没时间整理了,简单新建了个项目,有用的东西都放在里面了,要自己研究下
https://gitee.com/bunnya/custom-comp-template

天才, 出院
我本来想用一个属性 + onEnable 周期, 切 active 来控制的, 你酱更好