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.
334 lines
46 KiB
334 lines
46 KiB
|
1 week ago
|
"use strict";
|
||
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
|
exports.unload = exports.update = exports.load = exports.methods = void 0;
|
||
|
|
const cc_1 = require("cc");
|
||
|
|
const path_1 = require("path");
|
||
|
|
const Fs = require('fs');
|
||
|
|
const Path = require('path');
|
||
|
|
// 临时在当前模块增加编辑器内的模块为搜索路径,为了能够正常 require 到 cc 模块,后续版本将优化调用方式
|
||
|
|
module.paths.push((0, path_1.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 = cc_1.path.join(projectPath, 'assets', 'res', 'font', 'str.txt');
|
||
|
|
/**
|
||
|
|
* @en
|
||
|
|
* @zh 为扩展的主进程的注册方法
|
||
|
|
*/
|
||
|
|
exports.methods = {
|
||
|
|
log() { },
|
||
|
|
async printSpineAnim() {
|
||
|
|
var _a, _b;
|
||
|
|
let s = cc_1.director.getScene();
|
||
|
|
let cur = Editor.Selection.getLastSelected("node");
|
||
|
|
let node = this.getNodeByuuid(s, cur);
|
||
|
|
let spAnim = node.getComponent(cc_1.sp.Skeleton);
|
||
|
|
if (!spAnim)
|
||
|
|
return;
|
||
|
|
console.log("选中节点包含以下动画:");
|
||
|
|
//打印所有动画名
|
||
|
|
let anims = (_b = (_a = spAnim === null || spAnim === void 0 ? void 0 : spAnim.skeletonData) === null || _a === void 0 ? void 0 : _a.getRuntimeData()) === null || _b === void 0 ? void 0 : _b.animations;
|
||
|
|
anims === null || anims === void 0 ? void 0 : anims.forEach(x => {
|
||
|
|
console.log("动画名:" + x.name);
|
||
|
|
});
|
||
|
|
console.log("选中节点当前动画已复制成功:" + (spAnim === null || spAnim === void 0 ? void 0 : spAnim.animation));
|
||
|
|
//复制动画名称
|
||
|
|
Editor.Clipboard.write('text', spAnim === null || spAnim === void 0 ? void 0 : spAnim.animation);
|
||
|
|
},
|
||
|
|
async changeNodeName() {
|
||
|
|
let s = cc_1.director.getScene();
|
||
|
|
let cur = Editor.Selection.getLastSelected("node");
|
||
|
|
let node = this.getNodeByuuid(s, cur);
|
||
|
|
if (!node)
|
||
|
|
return;
|
||
|
|
console.log("选中节点:" + node.name);
|
||
|
|
let name = Editor.Clipboard.read('text');
|
||
|
|
console.log("剪切板内容:" + name);
|
||
|
|
//设置node的名字为剪切板内容
|
||
|
|
node.name = name + "";
|
||
|
|
},
|
||
|
|
async uiScript() {
|
||
|
|
let s = cc_1.director.getScene();
|
||
|
|
let cur = Editor.Selection.getLastSelected("node");
|
||
|
|
console.log("选中节点:" + cur);
|
||
|
|
let node = this.getNodeByuuid(s, cur);
|
||
|
|
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) => 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) => 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, 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 = [];
|
||
|
|
for (let i = 0; i < root.children.length; i++) {
|
||
|
|
let c = 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(cc_1.Label) != null && c.name.indexOf("lb_") >= 0) {
|
||
|
|
typ = "Label";
|
||
|
|
}
|
||
|
|
if (c.getComponent(cc_1.RichText) != null && c.name.indexOf("rt_") >= 0) {
|
||
|
|
typ = "RichText";
|
||
|
|
}
|
||
|
|
if (c.getComponent(cc_1.EditBox) != null && c.name.indexOf("eb_") >= 0) {
|
||
|
|
typ = "EditBox";
|
||
|
|
}
|
||
|
|
if (c.getComponent(cc_1.Sprite) != null && (c.name.indexOf("img_") >= 0 || c.name.indexOf("sp_") >= 0)) {
|
||
|
|
typ = "Sprite";
|
||
|
|
}
|
||
|
|
if (c.getComponent(cc_1.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(cc_1.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;
|
||
|
|
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 = cc_1.path.join(projectPath, 'assets');
|
||
|
|
if (Fs.existsSync(assetsPath)) {
|
||
|
|
this.traverseDirectory(assetsPath, usedChars);
|
||
|
|
}
|
||
|
|
// 将字符集合转换为字符串
|
||
|
|
const charsString = Array.from(usedChars).join('');
|
||
|
|
// 确保输出目录存在
|
||
|
|
const outputDir = cc_1.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) => {
|
||
|
|
const filePath = cc_1.path.join(dirPath, file);
|
||
|
|
const stat = Fs.statSync(filePath);
|
||
|
|
if (stat.isDirectory()) {
|
||
|
|
// 如果是目录,递归遍历
|
||
|
|
this.traverseDirectory(filePath, usedChars);
|
||
|
|
}
|
||
|
|
else if (stat.isFile()) {
|
||
|
|
// 如果是文件,读取内容并提取字符
|
||
|
|
const ext = cc_1.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 扩展加载完成后触发的钩子
|
||
|
|
*/
|
||
|
|
const load = function () {
|
||
|
|
let id = Editor.Selection.getLastSelected("Node");
|
||
|
|
console.log("选中节点:" + id);
|
||
|
|
};
|
||
|
|
exports.load = load;
|
||
|
|
const update = function () {
|
||
|
|
let id = Editor.Selection.getLastSelected("Node");
|
||
|
|
console.log("选中节点:" + id);
|
||
|
|
};
|
||
|
|
exports.update = update;
|
||
|
|
/**
|
||
|
|
* @en Hooks triggered after extension uninstallation is complete
|
||
|
|
* @zh 扩展卸载完成后触发的钩子
|
||
|
|
*/
|
||
|
|
const unload = function () { };
|
||
|
|
exports.unload = unload;
|
||
|
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2NlbmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zb3VyY2Uvc2NlbmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsMkJBQTJIO0FBQzNILCtCQUE0QjtBQUM1QixNQUFNLEVBQUUsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7QUFDekIsTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0FBRzdCLDJEQUEyRDtBQUMzRCxNQUFNLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFBLFdBQUksRUFBQyxNQUFNLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxjQUFjLENBQUMsQ0FBQyxDQUFDO0FBRXpELHVDQUF1QztBQUN2QyxpRUFBaUU7QUFDakUscURBQXFEO0FBRXJELFNBQVM7QUFDVCxNQUFNLFdBQVcsR0FBRyxNQUFNLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQztBQUV4QyxXQUFXO0FBQ1gsTUFBTSxVQUFVLEdBQUcsU0FBSSxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsU0FBUyxDQUFDLENBQUM7QUFHOUU7OztHQUdHO0FBQ1UsUUFBQSxPQUFPLEdBQTRDO0lBQzlELEdBQUcsS0FBSyxDQUFDO0lBQ1QsS0FBSyxDQUFDLGNBQWM7O1FBQ2xCLElBQUksQ0FBQyxHQUFHLGFBQVEsQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUM1QixJQUFJLEdBQUcsR0FBRyxNQUFNLENBQUMsU0FBUyxDQUFDLGVBQWUsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUNuRCxJQUFJLElBQUksR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLENBQUMsRUFBRSxHQUFHLENBQVMsQ0FBQztRQUM5QyxJQUFJLE1BQU0sR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLE9BQUUsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUM1QyxJQUFJLENBQUMsTUFBTTtZQUFFLE9BQU87UUFDcEIs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
|