I managed to make it work by looking at other examples around the forum. I create a custom editor class that will call any method inside another component, without entering play mode.
It looks like this (ignore Test 2 button)
It will use the node id of a node, search for a component with a specific component name, then call a method inside that component.
If Node UUID is empty, it will use the current selection inside editor click.
Just incase someone stuck in this in the future, here are my steps
import { _decorator, Component, Node } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('MyCustomEditor')
export class MyCustomEditor extends Component {
@property({tooltip : "hi"})
componentName = '';
@property
methodName = '';
@property
nodeUUID = '';
}
First you need to create class inside your project folder
Then create an extention
Then create a link to custom class inside package.json
{
"package_version": 2,
"version": "1.0.0",
"name": "custom-button",
"description": "i18n:custom-button.description",
"main": "./dist/main.js",
"devDependencies": {
"@types/node": "^16.0.1",
"typescript": "^4.3.4"
},
"author": "Cocos Creator",
"editor": ">=3.8.1",
"scripts": {
"build": "tsc -b",
"watch": "tsc -w"
},
"contributions": {
"inspector": {
"section": {
"node": {
"MyCustomEditor" : "./dist/contributions/inspector/MyCustomEditor.js"
}
}
}
}
}
then create this custom class in a specific path. Mine is:
F:\Cocos Projects\TestCocosAsset\extensions\custom-button\src\contributions\inspector
Then run cmd command “npm run build” inside the extention folder.
Then reload the engine.
// @ts-nocheck
"use strict";
import { INode, Vec3 } from "../../../@types/packages/scene/@types/public";
type Selector<$> = { $: Record<keyof $, any | null> };
export const template = `
<div>
<br>
<ui-prop type="dump" id="label1"></ui-prop>
<ui-prop type="dump" id="label2"></ui-prop>
<ui-prop type="dump" id="label3"></ui-prop>
<br>
</div>
<div style="display:flex;justify-content:center;align-items:center;margin-top:10px;">
<ui-button id="btn1" style="height:24px;padding:0 16px;margin: 5px 10px">Call method</ui-button>
<br>
<ui-button id="btn2" style="height:24px;padding:0 16px;margin: 5px 10px">Test 2</ui-button>
</div>
`;
export const $ = {
btn1: "#btn1",
btn2: "#btn2",
label1: "#label1",
label2: "#label2",
label3: "#label3",
};
type PanelThis = Selector<typeof $> & { dump: any };
export function update(this: PanelThis, dump: any) {
// Cache 'dump' data, please hang on 'this', otherwise there may be problems when multiple opening
this.dump = dump;
// Pass the 'dump' data to the prop element that helped submit the data
this.$.label1.dump = dump.value.componentName;
this.$.label1.render(dump.value.componentName);
this.$.label2.dump = dump.value.methodName;
this.$.label2.render(dump.value.methodName);
this.$.label3.dump = dump.value.nodeUUID;
this.$.label3.render(dump.value.nodeUUID);
// if (typeof this.$.editor.render === "function") {
// this.$.editor.render(dump.value.label);
// }
}
export function ready(this: Selector<typeof $> & any) {
this.$.btn1.addEventListener("confirm", async () => {
let selectedNode: string = '';
if (this.dump.value.nodeUUID.value) {
selectedNode = this.dump.value.nodeUUID.value;
}
else {
selectedNode = Editor.Selection.getSelected("node");
}
const node: INode = await Editor.Message.request("scene", "query-node", selectedNode);
//const node: INode = await Editor.Message.request("scene", "query-node", Editor.Selection.getSelected("node"));
if (!node) {
console.warn(`no node selected`);
return;
}
const ComponentName = this.dump.value.componentName.value;
const methodName = this.dump.value.methodName.value;
if(!ComponentName){
console.error("must enter component name!");
return;
}
if(!methodName){
console.error("must enter methodName name!");
return;
}
console.log(`Activate method: '${methodName}' inside component: '${ComponentName}'`);
const index = node.__comps__.findIndex((v: any) => v.type === ComponentName);
if (index === -1) {
console.warn(`Cannot find ${ComponentName} component on this node`);
return;
}
const component = node.__comps__[index];
const go = await Editor.Message.request("scene", "execute-component-method", {
uuid: component.value.uuid.value,
name: methodName,
});
});
this.$.btn2.addEventListener("confirm", async () => {
console.log("btn2 OK");
});
}