You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
371 lines
12 KiB
371 lines
12 KiB
import { Button, director, EditBox, Label, Node, path, RichText, ScrollView, Sorting2D, sp, Sprite, UIRenderer } from 'cc';
|
|
import { join } from 'path';
|
|
const Fs = require('fs');
|
|
const Path = require('path');
|
|
|
|
|
|
// 临时在当前模块增加编辑器内的模块为搜索路径,为了能够正常 require 到 cc 模块,后续版本将优化调用方式
|
|
module.paths.push(join(Editor.App.path, 'node_modules'));
|
|
|
|
// 当前版本需要在 module.paths 修改后才能正常使用 cc 模块
|
|
// 并且如果希望正常显示 cc 的定义,需要手动将 engine 文件夹里的 cc.d.ts 添加到插件的 tsconfig 里
|
|
// 当前版本的 cc 定义文件可以在当前项目的 temp/declarations/cc.d.ts 找到
|
|
|
|
// 获取项目路径
|
|
const projectPath = Editor.Project.path;
|
|
|
|
// 定义输出文件路径
|
|
const outputPath = path.join(projectPath, 'assets', 'res', 'font', 'str.txt');
|
|
|
|
|
|
/**
|
|
* @en
|
|
* @zh 为扩展的主进程的注册方法
|
|
*/
|
|
export const methods: { [key: string]: (...any: any) => any } = {
|
|
log() { },
|
|
async printSpineAnim() {
|
|
let s = director.getScene();
|
|
let cur = Editor.Selection.getLastSelected("node");
|
|
let node = this.getNodeByuuid(s, cur) as Node;
|
|
let spAnim = node.getComponent(sp.Skeleton);
|
|
if (!spAnim) return;
|
|
console.log("选中节点包含以下动画:");
|
|
//打印所有动画名
|
|
let anims = spAnim?.skeletonData?.getRuntimeData()?.animations;
|
|
anims?.forEach(x => {
|
|
console.log("动画名:" + x.name);
|
|
});
|
|
console.log("选中节点当前动画已复制成功:" + spAnim?.animation);
|
|
//复制动画名称
|
|
Editor.Clipboard.write('text', spAnim?.animation);
|
|
},
|
|
async changeNodeName() {
|
|
let s = director.getScene();
|
|
let cur = Editor.Selection.getLastSelected("node");
|
|
let node = this.getNodeByuuid(s, cur) as Node;
|
|
if (!node) return;
|
|
console.log("选中节点:" + node.name);
|
|
let name = Editor.Clipboard.read('text');
|
|
console.log("剪切板内容:" + name);
|
|
//设置node的名字为剪切板内容
|
|
node.name = name + "";
|
|
},
|
|
async uiScript() {
|
|
let s = director.getScene();
|
|
let cur = Editor.Selection.getLastSelected("node");
|
|
console.log("选中节点:" + cur);
|
|
let node = this.getNodeByuuid(s, cur) as Node;
|
|
let autoName = "Auto_" + node.name;
|
|
let nodes = this.getAllNodes(node, "node");
|
|
let funcNames = [];
|
|
//console.log("得到的所有节点", nodes);
|
|
let str = "";
|
|
str +=
|
|
`
|
|
//*********************
|
|
// create by 流云
|
|
// 插件自动生成的节点脚本
|
|
// 请勿修改(自动生成会覆盖)
|
|
// 请通过子类继承使用
|
|
// 如何生成可直接调用的UI节点和组件?
|
|
// 1.在场景节点树中选中要生成的UI根节点
|
|
// 2.点击菜单"tool"->“生成UI脚本” 或者使用快捷键(快捷键:Ctrl+Shift+C)
|
|
// 3.点击后会在script/ui/目录中生成一个以选中节点命名的文件夹,文件夹中会生成两个脚本文件,“Auto_”开头的就是本文件,不可修改,另一个是“UI”+选中节点名称命名的脚本文件,可修改,是需要挂载到选中节点的脚本文件,给用户使用的写逻辑的脚本,只会生成一次
|
|
// 4.找到第3步描述的“UI”+选中节点名称命名的脚本文件,将其挂载到选中节点上(第一生成需要挂载,后续修改场景,重复生成的时候,该脚本不会再生成,不用担心会覆盖逻辑脚本)
|
|
// 下面是类型示例
|
|
// 1.按钮,节点名以“btn_”或者“Btn_”开头或者节点添加了Button组件, 例:btn_Close,会生成btn_Close节点和click_btn_Close()方法
|
|
// 2.标签,节点名以“lb_”开头, 例:lb_title,会生成lb_title的Label组件
|
|
// 3.输入框,节点名以“eb_”开头, 例:eb_input,会生成eb_input的EditBox组件
|
|
// 4.滚动视图,节点名以“sv_”开头, 例:sv_list,会生成sv_list的ScrollView组件
|
|
// 5.图片,节点名以“img_”或者sp_“”开头, 例:img_Icon,会生成img_Icon的Sprite组件
|
|
// 6.普通节点,节点名以“ui_”或者“Ui_”或者“UI_”开头, 例:ui_Content,会生成ui_Content的Node节点
|
|
//*********************
|
|
import { _decorator, Component, Node, find, Sprite, Label, Button, ScrollView, EditBox, RichText } from 'cc';
|
|
import { UIBase } from '../../../mx/module/ui/UIBase';
|
|
const { ccclass } = _decorator;
|
|
|
|
@ccclass('` + autoName + `')
|
|
export class ` + autoName + ` extends UIBase {\r\n`;
|
|
|
|
console.log("得到的所有节点", JSON.stringify(nodes));
|
|
for (let i = 0; i < nodes.length; i++) {
|
|
let tName = nodes[i].nodeName;
|
|
if (nodes.filter((x: any) => x.nodeName == tName).length > 1) {
|
|
tName = nodes[i].path.replace(/\//g, "_");
|
|
}
|
|
let type = nodes[i].type != "" && nodes[i].type != "Button" ? nodes[i].type : "Node";
|
|
str += " " + tName + `:${type};\r\n`;
|
|
}
|
|
|
|
str += ` onLoad(){
|
|
super.onLoad();\r\n`;
|
|
for (let i = 0; i < nodes.length; i++) {
|
|
let tName = nodes[i].nodeName;
|
|
if (nodes.filter((x: any) => x.nodeName == tName).length > 1) {
|
|
tName = nodes[i].path.replace(/\//g, "_");
|
|
}
|
|
if (nodes[i].type == "Button") {
|
|
funcNames.push(tName);
|
|
}
|
|
let lastStr = "";
|
|
if (nodes[i].type == "ScrollView") {
|
|
lastStr = "?.getComponent(ScrollView)";
|
|
}
|
|
if (nodes[i].type == "Label") {
|
|
lastStr = "?.getComponent(Label)";
|
|
}
|
|
if (nodes[i].type == "EditBox") {
|
|
lastStr = "?.getComponent(EditBox)";
|
|
}
|
|
if (nodes[i].type == "Sprite") {
|
|
lastStr = "?.getComponent(Sprite)";
|
|
}
|
|
if (nodes[i].type == "Button") {
|
|
lastStr = "";
|
|
}
|
|
if (nodes[i].type == "RichText") {
|
|
lastStr = "?.getComponent(RichText)";
|
|
}
|
|
str += ` this.${tName} = find("${nodes[i].path}",this.node)${lastStr};\r\n`;
|
|
}
|
|
|
|
str += ` }\r\n`;
|
|
|
|
str += ` onEnable() {\r\n`;
|
|
for (let i = 0; i < funcNames.length; i++) {
|
|
str += ` this.${funcNames[i]}.on(Node.EventType.TOUCH_END, this.click_${funcNames[i]}, this);\r\n`;
|
|
}
|
|
str += ` }\r\n`;
|
|
|
|
str += ` onDisable() {\r\n`;
|
|
for (let i = 0; i < funcNames.length; i++) {
|
|
str += ` this.${funcNames[i]}.off(Node.EventType.TOUCH_END, this.click_${funcNames[i]}, this);\r\n`;
|
|
}
|
|
str += ` }\r\n`;
|
|
|
|
for (let i = 0; i < funcNames.length; i++) {
|
|
str += ` click_${funcNames[i]}() {}\r\n`;
|
|
}
|
|
|
|
str += `}`;
|
|
|
|
Editor.Message.send(
|
|
"asset-db",
|
|
"create-asset",
|
|
`db://assets/script/ui/${node.name}/${autoName}.ts`,
|
|
str,
|
|
{ overwrite: true }
|
|
);
|
|
|
|
let className = "UI" + node.name;
|
|
let path = `db://assets/script/ui/${node.name}/${className}.ts`;
|
|
let r = await Editor.Message.request(
|
|
"asset-db",
|
|
"query-asset-info",
|
|
path
|
|
);
|
|
console.log("生成成功?", r);
|
|
this.extractUsedChars();
|
|
if (r) return;
|
|
let comp = node.getComponent(className);
|
|
console.log("组件:", comp);
|
|
if (!comp) {
|
|
str = `
|
|
//*********************
|
|
// create by 流云
|
|
// time: ${new Date().toDateString()}
|
|
// desc:
|
|
//*********************
|
|
import { _decorator, Component, Node , find} from 'cc';
|
|
import { ${autoName} } from './${autoName}';
|
|
const { ccclass, property } = _decorator;
|
|
|
|
@ccclass('${className}')
|
|
export class ${className} extends ${autoName} {
|
|
|
|
onLoad(): void {
|
|
super.onLoad();
|
|
}
|
|
|
|
onEnable(): void {
|
|
super.onEnable();
|
|
}
|
|
|
|
onDisable(): void {
|
|
super.onDisable();
|
|
}
|
|
|
|
onInit(): void {
|
|
|
|
}
|
|
}
|
|
`;
|
|
Editor.Message.send(
|
|
"asset-db",
|
|
"create-asset",
|
|
path,
|
|
str
|
|
);
|
|
}
|
|
|
|
//node.addComponent(className);
|
|
},
|
|
|
|
getNodeByuuid(root: Node, uuid) {
|
|
let node = null;
|
|
for (let i = 0; i < root.children.length; i++) {
|
|
let c = root.children[i];
|
|
if (c.uuid == uuid) {
|
|
node = c;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (node != null) return node;
|
|
else {
|
|
for (let i = 0; i < root.children.length; i++) {
|
|
let c = root.children[i];
|
|
let n = this.getNodeByuuid(c, uuid);
|
|
if (n) return n;
|
|
}
|
|
}
|
|
},
|
|
|
|
getAllNodes(root, parentName, path = "") {
|
|
let tempArr: Array<{}> = [];
|
|
for (let i = 0; i < root.children.length; i++) {
|
|
let c: Node = root.children[i];
|
|
let p = path == "" ? c.name : path + "/" + c.name;
|
|
let typ = "";
|
|
// let sortComp = c.getComponent(Sorting2D);
|
|
|
|
// if (sortComp) {
|
|
// sortComp.destroy();
|
|
// }
|
|
|
|
if (c.getComponent(Label) != null && c.name.indexOf("lb_") >= 0) {
|
|
typ = "Label";
|
|
}
|
|
if (c.getComponent(RichText) != null && c.name.indexOf("rt_") >= 0) {
|
|
typ = "RichText";
|
|
}
|
|
if (c.getComponent(EditBox) != null && c.name.indexOf("eb_") >= 0) {
|
|
typ = "EditBox";
|
|
}
|
|
if (c.getComponent(Sprite) != null && (c.name.indexOf("img_") >= 0 || c.name.indexOf("sp_") >= 0)) {
|
|
typ = "Sprite";
|
|
}
|
|
if (c.getComponent(ScrollView) != null && c.name.indexOf("sv_") >= 0) {
|
|
typ = "ScrollView";
|
|
}
|
|
if (c.name.indexOf("ui_") >= 0 || c.name.indexOf("UI_") >= 0 || c.name.indexOf("Ui_") >= 0) {
|
|
typ = "Node";
|
|
}
|
|
if (c.getComponent(Button) != null || c.name.indexOf("btn_") >= 0 || c.name.indexOf("Btn_") >= 0) {
|
|
typ = "Button";
|
|
}
|
|
if (!/[\u4e00-\u9fff]/.test(c.name) && !/^\d/.test(c.name) && !/-/.test(c.name) && !/\s/.test(c.name) && (typ == "Button" || typ == "Label" || typ == "Sprite" || typ == "ScrollView" || typ == "Node" || typ == "EditBox" || typ == "RichText"))
|
|
tempArr.push({ nodeName: c.name, parentName: parentName, path: p, type: typ });
|
|
if (c.children.length > 0) {
|
|
let arr = this.getAllNodes(c, c.name, p);
|
|
tempArr = tempArr.concat(arr);
|
|
}
|
|
}
|
|
return tempArr;
|
|
},
|
|
|
|
/**
|
|
* 获取节点树
|
|
* @param {string} filePath 文件路径
|
|
* @returns {object}
|
|
*/
|
|
getNodeTree(filePath) {
|
|
// 获取缓存
|
|
let cache = this.cache as any;
|
|
if (!cache) {
|
|
cache = this.cache = Object.create(null);
|
|
}
|
|
// 从缓存中读取
|
|
if (!cache[filePath]) {
|
|
// 将资源数据转为节点树
|
|
const data = JSON.parse(Fs.readFileSync(filePath));
|
|
cache[filePath] = this.convertToNodeTree(data);
|
|
}
|
|
},
|
|
|
|
|
|
// 提取所有使用的字符
|
|
extractUsedChars() {
|
|
const usedChars = new Set();
|
|
|
|
// 遍历项目中的 assets 目录
|
|
const assetsPath = path.join(projectPath, 'assets');
|
|
if (Fs.existsSync(assetsPath)) {
|
|
this.traverseDirectory(assetsPath, usedChars);
|
|
}
|
|
|
|
// 将字符集合转换为字符串
|
|
const charsString = Array.from(usedChars).join('');
|
|
|
|
// 确保输出目录存在
|
|
const outputDir = path.dirname(outputPath);
|
|
if (!Fs.existsSync(outputDir)) {
|
|
Fs.mkdirSync(outputDir, { recursive: true });
|
|
}
|
|
|
|
// 写入文件
|
|
Fs.writeFileSync(outputPath, charsString);
|
|
|
|
console.log(`Extracted ${usedChars.size} unique characters to ${outputPath}`);
|
|
},
|
|
|
|
// 遍历目录并提取字符
|
|
traverseDirectory(dirPath, usedChars) {
|
|
const files = Fs.readdirSync(dirPath);
|
|
|
|
files.forEach((file: any) => {
|
|
const filePath = path.join(dirPath, file);
|
|
const stat = Fs.statSync(filePath);
|
|
|
|
if (stat.isDirectory()) {
|
|
// 如果是目录,递归遍历
|
|
this.traverseDirectory(filePath, usedChars);
|
|
} else if (stat.isFile()) {
|
|
// 如果是文件,读取内容并提取字符
|
|
const ext = path.extname(file).toLowerCase();
|
|
if (ext === '.js' || ext === '.ts' || ext === '.json' || ext === '.fire' || ext === '.prefab' || ext === '.csv' || ext === '.txt') {
|
|
try {
|
|
const content = Fs.readFileSync(filePath, 'utf-8');
|
|
for (let char of content) {
|
|
usedChars.add(char);
|
|
}
|
|
} catch (err) {
|
|
console.warn(`Failed to read file: ${filePath}, error: ${err}`);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* @en Hooks triggered after extension loading is complete
|
|
* @zh 扩展加载完成后触发的钩子
|
|
*/
|
|
export const load = function () {
|
|
let id = Editor.Selection.getLastSelected("Node");
|
|
console.log("选中节点:" + id);
|
|
};
|
|
|
|
export const update = function () {
|
|
let id = Editor.Selection.getLastSelected("Node");
|
|
console.log("选中节点:" + id);
|
|
}
|
|
|
|
/**
|
|
* @en Hooks triggered after extension uninstallation is complete
|
|
* @zh 扩展卸载完成后触发的钩子
|
|
*/
|
|
export const unload = function () { };
|
|
|