cocos2d-cli 1.0.4 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cocos-cli.js +18 -17
- package/package.json +4 -6
- package/src/commands/add.js +10 -6
- package/src/commands/delete.js +16 -15
- package/src/commands/get.js +152 -15
- package/src/commands/remove.js +11 -76
- package/src/commands/set.js +144 -30
- package/src/commands/tree.js +4 -72
- package/src/lib/fire-utils.js +65 -2
package/bin/cocos-cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
3
|
* Cocos Creator CLI
|
|
4
4
|
* Command-line tools for AI to read and manipulate Cocos Creator 2.4.x project scenes
|
|
5
5
|
*/
|
|
@@ -27,14 +27,14 @@ Cocos Creator CLI - 场景操作工具集
|
|
|
27
27
|
cocos2.4 <command> [options]
|
|
28
28
|
|
|
29
29
|
命令:
|
|
30
|
-
tree <场景文件路径>
|
|
31
|
-
get <场景文件路径>
|
|
32
|
-
set <场景文件路径>
|
|
33
|
-
add <场景文件路径>
|
|
34
|
-
add-component <场景文件路径>
|
|
35
|
-
remove <场景路径> <索引>
|
|
36
|
-
delete <场景文件路径>
|
|
37
|
-
build <项目目录>
|
|
30
|
+
tree <场景文件路径> 查看节点树(获取索引)
|
|
31
|
+
get <场景文件路径> <节点索引> 获取节点属性
|
|
32
|
+
set <场景文件路径> <节点索引> [选项] 修改节点属性
|
|
33
|
+
add <场景文件路径> <父节点索引> <名称> 添加节点到指定父节点
|
|
34
|
+
add-component <场景文件路径> <节点索引> <类型> 给节点添加组件
|
|
35
|
+
remove <场景路径> <索引> 删除节点或组件
|
|
36
|
+
delete <场景文件路径> <节点索引> 删除节点
|
|
37
|
+
build <项目目录> 构建组件映射
|
|
38
38
|
|
|
39
39
|
选项:
|
|
40
40
|
--name=<名称> 修改节点名称
|
|
@@ -50,19 +50,20 @@ Cocos Creator CLI - 场景操作工具集
|
|
|
50
50
|
--rotation=<角度> 修改旋转角度
|
|
51
51
|
--scaleX=<数值> 修改 X 缩放
|
|
52
52
|
--scaleY=<数值> 修改 Y 缩放
|
|
53
|
-
--type=sprite/label
|
|
54
|
-
--at
|
|
53
|
+
--type=sprite/label/button 添加节点时指定组件类型
|
|
54
|
+
--at=<索引> 添加节点时插入到子节点的指定位置(0=第一个)
|
|
55
55
|
|
|
56
56
|
示例:
|
|
57
57
|
cocos2.4 tree assets/main.fire
|
|
58
|
-
cocos2.4 get assets/main.fire
|
|
59
|
-
cocos2.4 set assets/main.fire
|
|
60
|
-
cocos2.4 add assets/main.fire
|
|
61
|
-
cocos2.4 add
|
|
62
|
-
cocos2.4
|
|
58
|
+
cocos2.4 get assets/main.fire 5
|
|
59
|
+
cocos2.4 set assets/main.fire 8 --x=100 --y=200
|
|
60
|
+
cocos2.4 add assets/main.fire 5 NewSprite --type=sprite --x=100
|
|
61
|
+
cocos2.4 add assets/main.fire 5 FirstChild --at=0
|
|
62
|
+
cocos2.4 add-component assets/main.fire 8 sprite
|
|
63
|
+
cocos2.4 delete assets/main.fire 12
|
|
63
64
|
cocos2.4 build ./my-project
|
|
64
65
|
|
|
65
|
-
版本: 1.0.
|
|
66
|
+
版本: 1.0.4
|
|
66
67
|
`);
|
|
67
68
|
}
|
|
68
69
|
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "cocos2d-cli",
|
|
3
|
-
"version": "1.0.
|
|
2
|
+
"name": "cocos2d-cli",
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Command-line tools for AI to read and manipulate Cocos Creator 2.4.x project scenes",
|
|
5
|
-
"main": "
|
|
5
|
+
"main": "bin/cocos-cli.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"cocos2d-cli": "./bin/cocos-cli.js"
|
|
8
8
|
},
|
|
@@ -12,9 +12,7 @@
|
|
|
12
12
|
"data"
|
|
13
13
|
],
|
|
14
14
|
"scripts": {
|
|
15
|
-
"test": "
|
|
16
|
-
"test:structure": "node test/verify_structure.js",
|
|
17
|
-
"test:cli": "bash test/run.sh"
|
|
15
|
+
"test": "echo \"No tests specified\" && exit 1"
|
|
18
16
|
},
|
|
19
17
|
"keywords": [
|
|
20
18
|
"cocos",
|
package/src/commands/add.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* add 命令 - 添加节点
|
|
2
|
+
* add 命令 - 添加节点
|
|
3
3
|
* 按照 Cocos Creator 的深度优先遍历顺序插入节点
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -102,11 +102,17 @@ function run(args) {
|
|
|
102
102
|
const data = loadScene(scenePath);
|
|
103
103
|
const { indexMap } = buildMaps(data);
|
|
104
104
|
|
|
105
|
+
// 父节点必须使用数字索引
|
|
106
|
+
if (!/^\d+$/.test(parentRef)) {
|
|
107
|
+
console.log(JSON.stringify({ error: '父节点必须使用数字索引,请先用 tree 命令查看节点索引' }));
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
105
111
|
// 查找父节点
|
|
106
|
-
const parentIndex =
|
|
112
|
+
const parentIndex = parseInt(parentRef);
|
|
107
113
|
|
|
108
|
-
if (parentIndex
|
|
109
|
-
console.log(JSON.stringify({ error:
|
|
114
|
+
if (parentIndex < 0 || parentIndex >= data.length || !data[parentIndex]) {
|
|
115
|
+
console.log(JSON.stringify({ error: `无效的节点索引: ${parentRef}` }));
|
|
110
116
|
return;
|
|
111
117
|
}
|
|
112
118
|
|
|
@@ -188,8 +194,6 @@ function run(args) {
|
|
|
188
194
|
|
|
189
195
|
const nodeName = isRoot ? 'Root' : (node._name || '(unnamed)');
|
|
190
196
|
const active = node._active !== false ? '●' : '○';
|
|
191
|
-
const connector = isRoot ? '' : (isLast ? '└── ' : '├── ');
|
|
192
|
-
|
|
193
197
|
let result = prefix + (isRoot ? '' : active + ' ') + nodeName + ' #' + nodeIndex;
|
|
194
198
|
|
|
195
199
|
// 添加组件信息
|
package/src/commands/delete.js
CHANGED
|
@@ -1,27 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* delete 命令 - 删除节点
|
|
2
|
+
* delete 命令 - 删除节点
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
const { loadScene, saveScene,
|
|
5
|
+
const { loadScene, saveScene, collectNodeAndChildren, rebuildReferences, refreshEditor, loadScriptMap, buildTree } = require('../lib/fire-utils');
|
|
6
6
|
|
|
7
7
|
function run(args) {
|
|
8
8
|
if (args.length < 2) {
|
|
9
|
-
console.log(JSON.stringify({ error: '用法: cocos2.4 delete <场景文件路径>
|
|
9
|
+
console.log(JSON.stringify({ error: '用法: cocos2.4 delete <场景文件路径> <节点索引>' }));
|
|
10
10
|
return;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
const scenePath = args[0];
|
|
14
14
|
const nodeRef = args[1];
|
|
15
15
|
|
|
16
|
+
// 节点必须使用数字索引
|
|
17
|
+
if (!/^\d+$/.test(nodeRef)) {
|
|
18
|
+
console.log(JSON.stringify({ error: '节点索引必须是数字,请先用 tree 命令查看节点索引' }));
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
16
22
|
try {
|
|
17
23
|
const data = loadScene(scenePath);
|
|
18
|
-
const { indexMap } = buildMaps(data);
|
|
19
24
|
|
|
20
|
-
|
|
21
|
-
const nodeIndex = findNodeIndex(data, indexMap, nodeRef);
|
|
25
|
+
const nodeIndex = parseInt(nodeRef);
|
|
22
26
|
|
|
23
|
-
if (
|
|
24
|
-
console.log(JSON.stringify({ error:
|
|
27
|
+
if (!data[nodeIndex]) {
|
|
28
|
+
console.log(JSON.stringify({ error: `无效的节点索引: ${nodeRef}` }));
|
|
25
29
|
return;
|
|
26
30
|
}
|
|
27
31
|
|
|
@@ -31,7 +35,6 @@ function run(args) {
|
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
const node = data[nodeIndex];
|
|
34
|
-
const nodeName = node._name;
|
|
35
38
|
|
|
36
39
|
// 收集所有需要删除的索引(节点 + 子节点 + 组件)
|
|
37
40
|
const indicesToDelete = collectNodeAndChildren(data, nodeIndex);
|
|
@@ -57,14 +60,12 @@ function run(args) {
|
|
|
57
60
|
// 保存场景
|
|
58
61
|
saveScene(scenePath, data);
|
|
59
62
|
|
|
60
|
-
//
|
|
63
|
+
// 触发编辑器刷新
|
|
61
64
|
refreshEditor(scenePath);
|
|
62
65
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
deletedCount: indicesToDelete.size
|
|
67
|
-
}, null, 2));
|
|
66
|
+
// 返回删除后的节点树
|
|
67
|
+
const scriptMap = loadScriptMap(scenePath);
|
|
68
|
+
console.log(buildTree(data, scriptMap, 1));
|
|
68
69
|
} catch (err) {
|
|
69
70
|
console.log(JSON.stringify({ error: err.message }));
|
|
70
71
|
}
|
package/src/commands/get.js
CHANGED
|
@@ -1,9 +1,126 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* get 命令 - 获取节点信息
|
|
2
|
+
* get 命令 - 获取节点信息
|
|
3
|
+
* 返回与编辑器 Inspector 面板一致的所有属性
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
6
|
const { loadScene, buildMaps, findNodeIndex } = require('../lib/fire-utils');
|
|
6
7
|
|
|
8
|
+
/**
|
|
9
|
+
* 将 _color 对象转为 #RRGGBB 字符串
|
|
10
|
+
*/
|
|
11
|
+
function colorToHex(color) {
|
|
12
|
+
if (!color) return '#ffffff';
|
|
13
|
+
const r = (color.r || 0).toString(16).padStart(2, '0');
|
|
14
|
+
const g = (color.g || 0).toString(16).padStart(2, '0');
|
|
15
|
+
const b = (color.b || 0).toString(16).padStart(2, '0');
|
|
16
|
+
return `#${r}${g}${b}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 提取组件的关键属性(对应 Inspector 面板显示)
|
|
21
|
+
*/
|
|
22
|
+
function extractComponentProps(comp) {
|
|
23
|
+
if (!comp) return null;
|
|
24
|
+
const type = comp.__type__;
|
|
25
|
+
|
|
26
|
+
// 只在 disabled 时才输出 enabled 字段
|
|
27
|
+
const base = comp._enabled ? { type } : { type, enabled: false };
|
|
28
|
+
|
|
29
|
+
// 清理对象中的 __type__ 字段
|
|
30
|
+
const clean = (obj) => {
|
|
31
|
+
if (!obj || typeof obj !== 'object') return obj;
|
|
32
|
+
if (Array.isArray(obj)) return obj.map(clean);
|
|
33
|
+
const result = {};
|
|
34
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
35
|
+
if (k === '__type__') continue;
|
|
36
|
+
result[k] = typeof v === 'object' ? clean(v) : v;
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
switch (type) {
|
|
42
|
+
case 'cc.Sprite':
|
|
43
|
+
return {
|
|
44
|
+
...base,
|
|
45
|
+
spriteFrame: comp._spriteFrame?.__uuid__ || null,
|
|
46
|
+
sizeMode: ['CUSTOM', 'TRIMMED', 'RAW'][comp._sizeMode] || comp._sizeMode,
|
|
47
|
+
spriteType: ['SIMPLE', 'SLICED', 'TILED', 'FILLED', 'MESH'][comp._type] || comp._type,
|
|
48
|
+
trim: comp._isTrimmedMode
|
|
49
|
+
};
|
|
50
|
+
case 'cc.Label':
|
|
51
|
+
return {
|
|
52
|
+
...base,
|
|
53
|
+
string: comp._string,
|
|
54
|
+
fontSize: comp._fontSize,
|
|
55
|
+
lineHeight: comp._lineHeight,
|
|
56
|
+
horizontalAlign: ['LEFT', 'CENTER', 'RIGHT'][comp._N$horizontalAlign] || comp._N$horizontalAlign,
|
|
57
|
+
verticalAlign: ['TOP', 'CENTER', 'BOTTOM'][comp._N$verticalAlign] || comp._N$verticalAlign,
|
|
58
|
+
overflow: ['NONE', 'CLAMP', 'SHRINK', 'RESIZE_HEIGHT'][comp._N$overflow] || comp._N$overflow,
|
|
59
|
+
fontFamily: comp._N$fontFamily,
|
|
60
|
+
enableWrapText: comp._enableWrapText
|
|
61
|
+
};
|
|
62
|
+
case 'cc.Button':
|
|
63
|
+
return {
|
|
64
|
+
...base,
|
|
65
|
+
interactable: comp._N$interactable,
|
|
66
|
+
transition: ['NONE', 'COLOR', 'SPRITE', 'SCALE'][comp._N$transition] || comp._N$transition,
|
|
67
|
+
zoomScale: comp.zoomScale,
|
|
68
|
+
duration: comp.duration
|
|
69
|
+
};
|
|
70
|
+
case 'cc.Widget':
|
|
71
|
+
return {
|
|
72
|
+
...base,
|
|
73
|
+
alignMode: ['ONCE', 'ON_WINDOW_RESIZE', 'ALWAYS'][comp.alignMode] || comp.alignMode,
|
|
74
|
+
left: comp._left,
|
|
75
|
+
right: comp._right,
|
|
76
|
+
top: comp._top,
|
|
77
|
+
bottom: comp._bottom
|
|
78
|
+
};
|
|
79
|
+
case 'cc.Layout':
|
|
80
|
+
return {
|
|
81
|
+
...base,
|
|
82
|
+
layoutType: ['NONE', 'HORIZONTAL', 'VERTICAL', 'GRID'][comp._N$layoutType] || comp._N$layoutType,
|
|
83
|
+
spacingX: comp._N$spacingX,
|
|
84
|
+
spacingY: comp._N$spacingY,
|
|
85
|
+
paddingLeft: comp._N$paddingLeft,
|
|
86
|
+
paddingRight: comp._N$paddingRight,
|
|
87
|
+
paddingTop: comp._N$paddingTop,
|
|
88
|
+
paddingBottom: comp._N$paddingBottom
|
|
89
|
+
};
|
|
90
|
+
case 'cc.Canvas':
|
|
91
|
+
return {
|
|
92
|
+
...base,
|
|
93
|
+
designResolution: clean(comp._designResolution),
|
|
94
|
+
fitWidth: comp._fitWidth,
|
|
95
|
+
fitHeight: comp._fitHeight
|
|
96
|
+
};
|
|
97
|
+
case 'cc.Camera':
|
|
98
|
+
return {
|
|
99
|
+
...base,
|
|
100
|
+
depth: comp._depth,
|
|
101
|
+
zoomRatio: comp._zoomRatio,
|
|
102
|
+
ortho: comp._ortho,
|
|
103
|
+
cullingMask: comp._cullingMask
|
|
104
|
+
};
|
|
105
|
+
case 'cc.ParticleSystem':
|
|
106
|
+
return {
|
|
107
|
+
...base,
|
|
108
|
+
playOnLoad: comp.playOnLoad,
|
|
109
|
+
totalParticles: comp.totalParticles,
|
|
110
|
+
duration: comp.duration
|
|
111
|
+
};
|
|
112
|
+
default:
|
|
113
|
+
// 未知组件:返回去掉内部字段的原始属性
|
|
114
|
+
const result = { ...base };
|
|
115
|
+
for (const key of Object.keys(comp)) {
|
|
116
|
+
if (!key.startsWith('_') && key !== '__type__') {
|
|
117
|
+
result[key] = comp[key];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
7
124
|
function run(args) {
|
|
8
125
|
if (args.length < 2) {
|
|
9
126
|
console.log(JSON.stringify({ error: '用法: cocos2.4 get <场景文件路径> <节点索引|名称>' }));
|
|
@@ -30,21 +147,41 @@ function run(args) {
|
|
|
30
147
|
console.log(JSON.stringify({ error: `节点不存在: ${nodeRef}` }));
|
|
31
148
|
return;
|
|
32
149
|
}
|
|
33
|
-
|
|
34
|
-
//
|
|
150
|
+
|
|
151
|
+
// 从 _trs 数组解析变换属性
|
|
152
|
+
// 格式: [x, y, z, qx, qy, qz, qw, scaleX, scaleY, scaleZ]
|
|
153
|
+
const trs = node._trs?.array || [0,0,0, 0,0,0,1, 1,1,1];
|
|
154
|
+
|
|
155
|
+
// 组件详细信息
|
|
156
|
+
const components = (node._components || []).map(ref => {
|
|
157
|
+
const comp = data[ref.__id__];
|
|
158
|
+
return extractComponentProps(comp);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// 子节点名称
|
|
162
|
+
const children = (node._children || []).map(ref => data[ref.__id__]?._name || '(unknown)');
|
|
163
|
+
|
|
35
164
|
const info = indexMap[idx] || {};
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
165
|
+
|
|
166
|
+
// 精简 JSON 输出
|
|
167
|
+
const result = {
|
|
168
|
+
name: info.name,
|
|
169
|
+
active: node._active,
|
|
170
|
+
position: { x: trs[0], y: trs[1] },
|
|
171
|
+
rotation: node._eulerAngles?.z ?? 0,
|
|
172
|
+
scale: { x: trs[7], y: trs[8] },
|
|
173
|
+
anchor: { x: node._anchorPoint?.x ?? 0.5, y: node._anchorPoint?.y ?? 0.5 },
|
|
174
|
+
size: { w: node._contentSize?.width ?? 0, h: node._contentSize?.height ?? 0 },
|
|
175
|
+
color: colorToHex(node._color),
|
|
176
|
+
opacity: node._opacity ?? 255,
|
|
177
|
+
group: node._groupIndex ?? 0
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
if (children.length > 0) result.children = children;
|
|
181
|
+
if (components.length > 0) result.components = components;
|
|
182
|
+
|
|
183
|
+
console.log(JSON.stringify(result));
|
|
184
|
+
|
|
48
185
|
} catch (err) {
|
|
49
186
|
console.log(JSON.stringify({ error: err.message }));
|
|
50
187
|
}
|
package/src/commands/remove.js
CHANGED
|
@@ -2,96 +2,31 @@
|
|
|
2
2
|
* remove 命令 - 删除节点或组件
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
const { loadScene, saveScene,
|
|
6
|
-
const fs = require('fs');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
|
|
9
|
-
// 加载脚本映射
|
|
10
|
-
function loadScriptMap(scenePath) {
|
|
11
|
-
const projectPath = path.dirname(path.dirname(scenePath));
|
|
12
|
-
const mapPath = path.join(projectPath, 'data', 'script_map.json');
|
|
13
|
-
try {
|
|
14
|
-
if (fs.existsSync(mapPath)) {
|
|
15
|
-
return JSON.parse(fs.readFileSync(mapPath, 'utf-8'));
|
|
16
|
-
}
|
|
17
|
-
} catch (e) {}
|
|
18
|
-
return {};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// 构建树形结构
|
|
22
|
-
function buildTree(data, scriptMap, nodeIndex, prefix = '', isLast = true, isRoot = true) {
|
|
23
|
-
const node = data[nodeIndex];
|
|
24
|
-
if (!node) return '';
|
|
25
|
-
|
|
26
|
-
const nodeName = isRoot ? 'Root' : (node._name || '(unnamed)');
|
|
27
|
-
const active = node._active !== false ? '●' : '○';
|
|
28
|
-
const uuidRegex = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i;
|
|
29
|
-
|
|
30
|
-
let result = prefix + (isRoot ? '' : active + ' ') + nodeName + ' #' + nodeIndex;
|
|
31
|
-
|
|
32
|
-
// 添加组件信息
|
|
33
|
-
if (node._components && node._components.length > 0) {
|
|
34
|
-
const comps = node._components.map(c => {
|
|
35
|
-
const comp = data[c.__id__];
|
|
36
|
-
if (!comp) return `? #${c.__id__}`;
|
|
37
|
-
const typeName = comp.__type__;
|
|
38
|
-
let displayName;
|
|
39
|
-
if (uuidRegex.test(typeName)) {
|
|
40
|
-
const scriptInfo = scriptMap[typeName];
|
|
41
|
-
if (scriptInfo && scriptInfo.name) {
|
|
42
|
-
displayName = scriptInfo.name;
|
|
43
|
-
} else {
|
|
44
|
-
displayName = '⚠️MissingScript';
|
|
45
|
-
}
|
|
46
|
-
} else if (typeName === 'MissingScript') {
|
|
47
|
-
displayName = '⚠️MissingScript';
|
|
48
|
-
} else {
|
|
49
|
-
displayName = typeName.replace('cc.', '');
|
|
50
|
-
}
|
|
51
|
-
return `${displayName} #${c.__id__}`;
|
|
52
|
-
}).join(', ');
|
|
53
|
-
result += ` (${comps})`;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
result += '\n';
|
|
57
|
-
|
|
58
|
-
// 处理子节点
|
|
59
|
-
if (node._children && node._children.length > 0) {
|
|
60
|
-
node._children.forEach((childRef, idx) => {
|
|
61
|
-
const childIsLast = idx === node._children.length - 1;
|
|
62
|
-
const childPrefix = prefix + (isRoot ? '' : (isLast ? ' ' : '│ '));
|
|
63
|
-
result += buildTree(data, scriptMap, childRef.__id__, childPrefix, childIsLast, false);
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return result;
|
|
68
|
-
}
|
|
5
|
+
const { loadScene, saveScene, collectNodeAndChildren, rebuildReferences, refreshEditor, loadScriptMap, buildTree } = require('../lib/fire-utils');
|
|
69
6
|
|
|
70
7
|
function run(args) {
|
|
71
8
|
if (args.length < 2) {
|
|
72
|
-
console.log('用法: cocos2.4 remove <场景文件路径> <索引>');
|
|
9
|
+
console.log(JSON.stringify({ error: '用法: cocos2.4 remove <场景文件路径> <索引>' }));
|
|
73
10
|
return;
|
|
74
11
|
}
|
|
75
12
|
|
|
76
13
|
const scenePath = args[0];
|
|
77
14
|
|
|
78
|
-
//
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const index = parseInt(args[1]);
|
|
82
|
-
|
|
83
|
-
if (isNaN(index)) {
|
|
84
|
-
console.log('错误: 索引必须是数字');
|
|
15
|
+
// 索引必须是数字
|
|
16
|
+
if (!/^\d+$/.test(args[1])) {
|
|
17
|
+
console.log(JSON.stringify({ error: '索引必须是数字,请先用 tree 命令查看节点索引' }));
|
|
85
18
|
return;
|
|
86
19
|
}
|
|
87
20
|
|
|
21
|
+
const index = parseInt(args[1]);
|
|
22
|
+
|
|
88
23
|
try {
|
|
89
24
|
let data = loadScene(scenePath);
|
|
90
25
|
const scriptMap = loadScriptMap(scenePath);
|
|
91
26
|
|
|
92
27
|
// 检查索引是否存在
|
|
93
28
|
if (!data[index]) {
|
|
94
|
-
console.log(
|
|
29
|
+
console.log(JSON.stringify({ error: `索引 ${index} 不存在` }));
|
|
95
30
|
return;
|
|
96
31
|
}
|
|
97
32
|
|
|
@@ -103,7 +38,7 @@ function run(args) {
|
|
|
103
38
|
const isComponent = item.node !== undefined;
|
|
104
39
|
|
|
105
40
|
if (isNode && index <= 1) {
|
|
106
|
-
console.log('
|
|
41
|
+
console.log(JSON.stringify({ error: '不能删除根节点' }));
|
|
107
42
|
return;
|
|
108
43
|
}
|
|
109
44
|
|
|
@@ -156,8 +91,8 @@ function run(args) {
|
|
|
156
91
|
console.log(buildTree(data, scriptMap, 1));
|
|
157
92
|
|
|
158
93
|
} catch (err) {
|
|
159
|
-
console.log(
|
|
94
|
+
console.log(JSON.stringify({ error: err.message }));
|
|
160
95
|
}
|
|
161
96
|
}
|
|
162
97
|
|
|
163
|
-
module.exports = { run };
|
|
98
|
+
module.exports = { run };
|
package/src/commands/set.js
CHANGED
|
@@ -1,9 +1,122 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* set 命令 - 修改节点属性
|
|
2
|
+
* set 命令 - 修改节点属性
|
|
3
|
+
* 修改成功后返回节点最终状态(与 get 命令格式一致)
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
6
|
const { loadScene, saveScene, buildMaps, findNodeIndex, refreshEditor } = require('../lib/fire-utils');
|
|
6
7
|
|
|
8
|
+
/**
|
|
9
|
+
* 将 _color 对象转为 #RRGGBB 字符串
|
|
10
|
+
*/
|
|
11
|
+
function colorToHex(color) {
|
|
12
|
+
if (!color) return '#ffffff';
|
|
13
|
+
const r = (color.r || 0).toString(16).padStart(2, '0');
|
|
14
|
+
const g = (color.g || 0).toString(16).padStart(2, '0');
|
|
15
|
+
const b = (color.b || 0).toString(16).padStart(2, '0');
|
|
16
|
+
return `#${r}${g}${b}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 提取组件的关键属性(对应 Inspector 面板显示)
|
|
21
|
+
*/
|
|
22
|
+
function extractComponentProps(comp) {
|
|
23
|
+
if (!comp) return null;
|
|
24
|
+
const type = comp.__type__;
|
|
25
|
+
const base = comp._enabled ? { type } : { type, enabled: false };
|
|
26
|
+
|
|
27
|
+
const clean = (obj) => {
|
|
28
|
+
if (!obj || typeof obj !== 'object') return obj;
|
|
29
|
+
if (Array.isArray(obj)) return obj.map(clean);
|
|
30
|
+
const result = {};
|
|
31
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
32
|
+
if (k === '__type__') continue;
|
|
33
|
+
result[k] = typeof v === 'object' ? clean(v) : v;
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
switch (type) {
|
|
39
|
+
case 'cc.Sprite':
|
|
40
|
+
return {
|
|
41
|
+
...base,
|
|
42
|
+
spriteFrame: comp._spriteFrame?.__uuid__ || null,
|
|
43
|
+
sizeMode: ['CUSTOM', 'TRIMMED', 'RAW'][comp._sizeMode] || comp._sizeMode,
|
|
44
|
+
spriteType: ['SIMPLE', 'SLICED', 'TILED', 'FILLED', 'MESH'][comp._type] || comp._type,
|
|
45
|
+
trim: comp._isTrimmedMode
|
|
46
|
+
};
|
|
47
|
+
case 'cc.Label':
|
|
48
|
+
return {
|
|
49
|
+
...base,
|
|
50
|
+
string: comp._string,
|
|
51
|
+
fontSize: comp._fontSize,
|
|
52
|
+
lineHeight: comp._lineHeight,
|
|
53
|
+
horizontalAlign: ['LEFT', 'CENTER', 'RIGHT'][comp._N$horizontalAlign] || comp._N$horizontalAlign,
|
|
54
|
+
verticalAlign: ['TOP', 'CENTER', 'BOTTOM'][comp._N$verticalAlign] || comp._N$verticalAlign,
|
|
55
|
+
overflow: ['NONE', 'CLAMP', 'SHRINK', 'RESIZE_HEIGHT'][comp._N$overflow] || comp._N$overflow,
|
|
56
|
+
fontFamily: comp._N$fontFamily,
|
|
57
|
+
enableWrapText: comp._enableWrapText
|
|
58
|
+
};
|
|
59
|
+
case 'cc.Button':
|
|
60
|
+
return {
|
|
61
|
+
...base,
|
|
62
|
+
interactable: comp._N$interactable,
|
|
63
|
+
transition: ['NONE', 'COLOR', 'SPRITE', 'SCALE'][comp._N$transition] || comp._N$transition,
|
|
64
|
+
zoomScale: comp.zoomScale,
|
|
65
|
+
duration: comp.duration
|
|
66
|
+
};
|
|
67
|
+
case 'cc.Widget':
|
|
68
|
+
return {
|
|
69
|
+
...base,
|
|
70
|
+
alignMode: ['ONCE', 'ON_WINDOW_RESIZE', 'ALWAYS'][comp.alignMode] || comp.alignMode,
|
|
71
|
+
left: comp._left,
|
|
72
|
+
right: comp._right,
|
|
73
|
+
top: comp._top,
|
|
74
|
+
bottom: comp._bottom
|
|
75
|
+
};
|
|
76
|
+
case 'cc.Layout':
|
|
77
|
+
return {
|
|
78
|
+
...base,
|
|
79
|
+
layoutType: ['NONE', 'HORIZONTAL', 'VERTICAL', 'GRID'][comp._N$layoutType] || comp._N$layoutType,
|
|
80
|
+
spacingX: comp._N$spacingX,
|
|
81
|
+
spacingY: comp._N$spacingY,
|
|
82
|
+
paddingLeft: comp._N$paddingLeft,
|
|
83
|
+
paddingRight: comp._N$paddingRight,
|
|
84
|
+
paddingTop: comp._N$paddingTop,
|
|
85
|
+
paddingBottom: comp._N$paddingBottom
|
|
86
|
+
};
|
|
87
|
+
case 'cc.Canvas':
|
|
88
|
+
return {
|
|
89
|
+
...base,
|
|
90
|
+
designResolution: clean(comp._designResolution),
|
|
91
|
+
fitWidth: comp._fitWidth,
|
|
92
|
+
fitHeight: comp._fitHeight
|
|
93
|
+
};
|
|
94
|
+
case 'cc.Camera':
|
|
95
|
+
return {
|
|
96
|
+
...base,
|
|
97
|
+
depth: comp._depth,
|
|
98
|
+
zoomRatio: comp._zoomRatio,
|
|
99
|
+
ortho: comp._ortho,
|
|
100
|
+
cullingMask: comp._cullingMask
|
|
101
|
+
};
|
|
102
|
+
case 'cc.ParticleSystem':
|
|
103
|
+
return {
|
|
104
|
+
...base,
|
|
105
|
+
playOnLoad: comp.playOnLoad,
|
|
106
|
+
totalParticles: comp.totalParticles,
|
|
107
|
+
duration: comp.duration
|
|
108
|
+
};
|
|
109
|
+
default:
|
|
110
|
+
const result = { ...base };
|
|
111
|
+
for (const key of Object.keys(comp)) {
|
|
112
|
+
if (!key.startsWith('_') && key !== '__type__') {
|
|
113
|
+
result[key] = comp[key];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
7
120
|
// 解析颜色
|
|
8
121
|
function parseColor(colorStr) {
|
|
9
122
|
if (!colorStr) return null;
|
|
@@ -22,6 +135,33 @@ function parseColor(colorStr) {
|
|
|
22
135
|
return null;
|
|
23
136
|
}
|
|
24
137
|
|
|
138
|
+
/**
|
|
139
|
+
* 获取节点的完整状态(与 get 命令一致)
|
|
140
|
+
*/
|
|
141
|
+
function getNodeState(data, node, nodeIndex) {
|
|
142
|
+
const trs = node._trs?.array || [0,0,0, 0,0,0,1, 1,1,1];
|
|
143
|
+
const components = (node._components || []).map(ref => extractComponentProps(data[ref.__id__]));
|
|
144
|
+
const children = (node._children || []).map(ref => data[ref.__id__]?._name || '(unknown)');
|
|
145
|
+
|
|
146
|
+
const result = {
|
|
147
|
+
name: node._name,
|
|
148
|
+
active: node._active,
|
|
149
|
+
position: { x: trs[0], y: trs[1] },
|
|
150
|
+
rotation: node._eulerAngles?.z ?? 0,
|
|
151
|
+
scale: { x: trs[7], y: trs[8] },
|
|
152
|
+
anchor: { x: node._anchorPoint?.x ?? 0.5, y: node._anchorPoint?.y ?? 0.5 },
|
|
153
|
+
size: { w: node._contentSize?.width ?? 0, h: node._contentSize?.height ?? 0 },
|
|
154
|
+
color: colorToHex(node._color),
|
|
155
|
+
opacity: node._opacity ?? 255,
|
|
156
|
+
group: node._groupIndex ?? 0
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
if (children.length > 0) result.children = children;
|
|
160
|
+
if (components.length > 0) result.components = components;
|
|
161
|
+
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
164
|
+
|
|
25
165
|
function run(args) {
|
|
26
166
|
if (args.length < 2) {
|
|
27
167
|
console.log(JSON.stringify({ error: '用法: cocos2.4 set <场景文件路径> <节点索引|名称> [选项]' }));
|
|
@@ -53,20 +193,15 @@ function run(args) {
|
|
|
53
193
|
}
|
|
54
194
|
|
|
55
195
|
const node = data[nodeIndex];
|
|
56
|
-
const changes = {};
|
|
57
196
|
|
|
58
197
|
// 修改名称
|
|
59
198
|
if (options.name !== undefined) {
|
|
60
|
-
const oldName = node._name;
|
|
61
199
|
node._name = options.name;
|
|
62
|
-
changes.name = { from: oldName, to: options.name };
|
|
63
200
|
}
|
|
64
201
|
|
|
65
202
|
// 修改激活状态
|
|
66
203
|
if (options.active !== undefined) {
|
|
67
|
-
const oldActive = node._active;
|
|
68
204
|
node._active = options.active !== 'false';
|
|
69
|
-
changes.active = { from: oldActive, to: node._active };
|
|
70
205
|
}
|
|
71
206
|
|
|
72
207
|
// 修改位置
|
|
@@ -74,11 +209,8 @@ function run(args) {
|
|
|
74
209
|
if (!node._trs) {
|
|
75
210
|
node._trs = { "__type__": "TypedArray", "ctor": "Float64Array", "array": [0, 0, 0, 0, 0, 0, 1, 1, 1, 1] };
|
|
76
211
|
}
|
|
77
|
-
const oldX = node._trs.array[0];
|
|
78
|
-
const oldY = node._trs.array[1];
|
|
79
212
|
if (options.x !== undefined) node._trs.array[0] = parseFloat(options.x);
|
|
80
213
|
if (options.y !== undefined) node._trs.array[1] = parseFloat(options.y);
|
|
81
|
-
changes.position = { from: [oldX, oldY], to: [node._trs.array[0], node._trs.array[1]] };
|
|
82
214
|
}
|
|
83
215
|
|
|
84
216
|
// 修改大小
|
|
@@ -86,11 +218,8 @@ function run(args) {
|
|
|
86
218
|
if (!node._contentSize) {
|
|
87
219
|
node._contentSize = { "__type__": "cc.Size", width: 0, height: 0 };
|
|
88
220
|
}
|
|
89
|
-
const oldW = node._contentSize.width;
|
|
90
|
-
const oldH = node._contentSize.height;
|
|
91
221
|
if (options.width !== undefined) node._contentSize.width = parseFloat(options.width);
|
|
92
222
|
if (options.height !== undefined) node._contentSize.height = parseFloat(options.height);
|
|
93
|
-
changes.size = { from: { width: oldW, height: oldH }, to: { width: node._contentSize.width, height: node._contentSize.height } };
|
|
94
223
|
}
|
|
95
224
|
|
|
96
225
|
// 修改锚点
|
|
@@ -98,18 +227,13 @@ function run(args) {
|
|
|
98
227
|
if (!node._anchorPoint) {
|
|
99
228
|
node._anchorPoint = { "__type__": "cc.Vec2", x: 0.5, y: 0.5 };
|
|
100
229
|
}
|
|
101
|
-
const oldX = node._anchorPoint.x;
|
|
102
|
-
const oldY = node._anchorPoint.y;
|
|
103
230
|
if (options.anchorX !== undefined) node._anchorPoint.x = parseFloat(options.anchorX);
|
|
104
231
|
if (options.anchorY !== undefined) node._anchorPoint.y = parseFloat(options.anchorY);
|
|
105
|
-
changes.anchor = { from: [oldX, oldY], to: [node._anchorPoint.x, node._anchorPoint.y] };
|
|
106
232
|
}
|
|
107
233
|
|
|
108
234
|
// 修改透明度
|
|
109
235
|
if (options.opacity !== undefined) {
|
|
110
|
-
const oldOpacity = node._opacity;
|
|
111
236
|
node._opacity = Math.max(0, Math.min(255, parseInt(options.opacity)));
|
|
112
|
-
changes.opacity = { from: oldOpacity, to: node._opacity };
|
|
113
237
|
}
|
|
114
238
|
|
|
115
239
|
// 修改颜色
|
|
@@ -117,7 +241,6 @@ function run(args) {
|
|
|
117
241
|
const color = parseColor(options.color);
|
|
118
242
|
if (color) {
|
|
119
243
|
node._color = color;
|
|
120
|
-
changes.color = { to: options.color };
|
|
121
244
|
}
|
|
122
245
|
}
|
|
123
246
|
|
|
@@ -126,9 +249,7 @@ function run(args) {
|
|
|
126
249
|
if (!node._eulerAngles) {
|
|
127
250
|
node._eulerAngles = { "__type__": "cc.Vec3", x: 0, y: 0, z: 0 };
|
|
128
251
|
}
|
|
129
|
-
const oldRotation = node._eulerAngles.z;
|
|
130
252
|
node._eulerAngles.z = parseFloat(options.rotation);
|
|
131
|
-
changes.rotation = { from: oldRotation, to: node._eulerAngles.z };
|
|
132
253
|
}
|
|
133
254
|
|
|
134
255
|
// 修改缩放
|
|
@@ -136,25 +257,18 @@ function run(args) {
|
|
|
136
257
|
if (!node._trs) {
|
|
137
258
|
node._trs = { "__type__": "TypedArray", "ctor": "Float64Array", "array": [0, 0, 0, 0, 0, 0, 1, 1, 1, 1] };
|
|
138
259
|
}
|
|
139
|
-
const oldScaleX = node._trs.array[7];
|
|
140
|
-
const oldScaleY = node._trs.array[8];
|
|
141
260
|
if (options.scaleX !== undefined) node._trs.array[7] = parseFloat(options.scaleX);
|
|
142
261
|
if (options.scaleY !== undefined) node._trs.array[8] = parseFloat(options.scaleY);
|
|
143
|
-
changes.scale = { from: [oldScaleX, oldScaleY], to: [node._trs.array[7], node._trs.array[8]] };
|
|
144
262
|
}
|
|
145
263
|
|
|
146
264
|
// 保存场景
|
|
147
265
|
saveScene(scenePath, data);
|
|
148
266
|
|
|
149
|
-
//
|
|
267
|
+
// 触发编辑器刷新
|
|
150
268
|
refreshEditor(scenePath);
|
|
151
269
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
index: nodeIndex,
|
|
155
|
-
name: node._name,
|
|
156
|
-
changes
|
|
157
|
-
}, null, 2));
|
|
270
|
+
// 返回节点最终状态(与 get 命令格式一致)
|
|
271
|
+
console.log(JSON.stringify(getNodeState(data, node, nodeIndex)));
|
|
158
272
|
} catch (err) {
|
|
159
273
|
console.log(JSON.stringify({ error: err.message }));
|
|
160
274
|
}
|
package/src/commands/tree.js
CHANGED
|
@@ -1,22 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* tree 命令 - 查看节点树
|
|
2
|
+
* tree 命令 - 查看节点树
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
const { loadScene,
|
|
6
|
-
const fs = require('fs');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
|
|
9
|
-
// 加载脚本映射
|
|
10
|
-
function loadScriptMap(scenePath) {
|
|
11
|
-
const projectPath = path.dirname(path.dirname(scenePath));
|
|
12
|
-
const mapPath = path.join(projectPath, 'data', 'script_map.json');
|
|
13
|
-
try {
|
|
14
|
-
if (fs.existsSync(mapPath)) {
|
|
15
|
-
return JSON.parse(fs.readFileSync(mapPath, 'utf-8'));
|
|
16
|
-
}
|
|
17
|
-
} catch (e) {}
|
|
18
|
-
return {};
|
|
19
|
-
}
|
|
5
|
+
const { loadScene, loadScriptMap, buildTree } = require('../lib/fire-utils');
|
|
20
6
|
|
|
21
7
|
function run(args) {
|
|
22
8
|
const scenePath = args[0];
|
|
@@ -28,64 +14,10 @@ function run(args) {
|
|
|
28
14
|
|
|
29
15
|
try {
|
|
30
16
|
const data = loadScene(scenePath);
|
|
31
|
-
const { indexMap } = buildMaps(data);
|
|
32
17
|
const scriptMap = loadScriptMap(scenePath);
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
// 构建树形结构输出
|
|
36
|
-
function buildTree(nodeIndex, prefix = '', isLast = true, isRoot = true) {
|
|
37
|
-
const node = data[nodeIndex];
|
|
38
|
-
if (!node) return '';
|
|
39
|
-
|
|
40
|
-
const nodeName = isRoot ? 'Root' : (node._name || '(unnamed)');
|
|
41
|
-
const active = node._active !== false ? '●' : '○';
|
|
42
|
-
const connector = isRoot ? '' : (isLast ? '└── ' : '├── ');
|
|
43
|
-
|
|
44
|
-
let result = prefix + (isRoot ? '' : active + ' ') + nodeName + ' #' + nodeIndex;
|
|
45
|
-
|
|
46
|
-
// 添加组件信息
|
|
47
|
-
if (node._components && node._components.length > 0) {
|
|
48
|
-
const comps = node._components.map(c => {
|
|
49
|
-
const comp = data[c.__id__];
|
|
50
|
-
if (!comp) return `? #${c.__id__}`;
|
|
51
|
-
const typeName = comp.__type__;
|
|
52
|
-
let displayName;
|
|
53
|
-
// 如果是 UUID,尝试从 scriptMap 查找类名
|
|
54
|
-
if (uuidRegex.test(typeName)) {
|
|
55
|
-
const scriptInfo = scriptMap[typeName];
|
|
56
|
-
if (scriptInfo && scriptInfo.name) {
|
|
57
|
-
displayName = scriptInfo.name;
|
|
58
|
-
} else {
|
|
59
|
-
displayName = '⚠️MissingScript';
|
|
60
|
-
}
|
|
61
|
-
} else if (typeName === 'MissingScript') {
|
|
62
|
-
displayName = '⚠️MissingScript';
|
|
63
|
-
} else {
|
|
64
|
-
displayName = typeName.replace('cc.', '');
|
|
65
|
-
}
|
|
66
|
-
return `${displayName} #${c.__id__}`;
|
|
67
|
-
}).join(', ');
|
|
68
|
-
result += ` (${comps})`;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
result += '\n';
|
|
72
|
-
|
|
73
|
-
// 处理子节点
|
|
74
|
-
if (node._children && node._children.length > 0) {
|
|
75
|
-
node._children.forEach((childRef, idx) => {
|
|
76
|
-
const childIsLast = idx === node._children.length - 1;
|
|
77
|
-
const childPrefix = prefix + (isRoot ? '' : (isLast ? ' ' : '│ '));
|
|
78
|
-
result += buildTree(childRef.__id__, childPrefix, childIsLast, false);
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return result;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const treeStr = buildTree(1);
|
|
86
|
-
console.log(treeStr);
|
|
18
|
+
console.log(buildTree(data, scriptMap, 1));
|
|
87
19
|
} catch (err) {
|
|
88
|
-
console.log(
|
|
20
|
+
console.log(JSON.stringify({ error: err.message }));
|
|
89
21
|
}
|
|
90
22
|
}
|
|
91
23
|
|
package/src/lib/fire-utils.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Fire 文件工具模块
|
|
2
|
+
* Fire 文件工具模块
|
|
3
3
|
* 提供直接读取和操作 .fire 场景文件的功能
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -398,6 +398,67 @@ function installPlugin(scenePath) {
|
|
|
398
398
|
}
|
|
399
399
|
}
|
|
400
400
|
|
|
401
|
+
/**
|
|
402
|
+
* 加载脚本映射(用于显示自定义脚本组件名称)
|
|
403
|
+
*/
|
|
404
|
+
function loadScriptMap(scenePath) {
|
|
405
|
+
const projectPath = path.dirname(path.dirname(scenePath));
|
|
406
|
+
const mapPath = path.join(projectPath, 'data', 'script_map.json');
|
|
407
|
+
try {
|
|
408
|
+
if (fs.existsSync(mapPath)) {
|
|
409
|
+
return JSON.parse(fs.readFileSync(mapPath, 'utf-8'));
|
|
410
|
+
}
|
|
411
|
+
} catch (e) {}
|
|
412
|
+
return {};
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* 构建节点树输出
|
|
417
|
+
*/
|
|
418
|
+
function buildTree(data, scriptMap, nodeIndex, prefix = '', isLast = true, isRoot = true) {
|
|
419
|
+
const node = data[nodeIndex];
|
|
420
|
+
if (!node) return '';
|
|
421
|
+
|
|
422
|
+
const nodeName = isRoot ? 'Root' : (node._name || '(unnamed)');
|
|
423
|
+
const active = node._active !== false ? '●' : '○';
|
|
424
|
+
const uuidRegex = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i;
|
|
425
|
+
|
|
426
|
+
let result = prefix + (isRoot ? '' : active + ' ') + nodeName + ' #' + nodeIndex;
|
|
427
|
+
|
|
428
|
+
// 添加组件信息
|
|
429
|
+
if (node._components && node._components.length > 0) {
|
|
430
|
+
const comps = node._components.map(c => {
|
|
431
|
+
const comp = data[c.__id__];
|
|
432
|
+
if (!comp) return `? #${c.__id__}`;
|
|
433
|
+
const typeName = comp.__type__;
|
|
434
|
+
let displayName;
|
|
435
|
+
if (uuidRegex.test(typeName)) {
|
|
436
|
+
const scriptInfo = scriptMap[typeName];
|
|
437
|
+
displayName = (scriptInfo && scriptInfo.name) ? scriptInfo.name : '⚠️MissingScript';
|
|
438
|
+
} else if (typeName === 'MissingScript') {
|
|
439
|
+
displayName = '⚠️MissingScript';
|
|
440
|
+
} else {
|
|
441
|
+
displayName = typeName.replace('cc.', '');
|
|
442
|
+
}
|
|
443
|
+
return `${displayName} #${c.__id__}`;
|
|
444
|
+
}).join(', ');
|
|
445
|
+
result += ` (${comps})`;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
result += '\n';
|
|
449
|
+
|
|
450
|
+
// 处理子节点
|
|
451
|
+
if (node._children && node._children.length > 0) {
|
|
452
|
+
node._children.forEach((childRef, idx) => {
|
|
453
|
+
const childIsLast = idx === node._children.length - 1;
|
|
454
|
+
const childPrefix = prefix + (isRoot ? '' : (isLast ? ' ' : '│ '));
|
|
455
|
+
result += buildTree(data, scriptMap, childRef.__id__, childPrefix, childIsLast, false);
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
return result;
|
|
460
|
+
}
|
|
461
|
+
|
|
401
462
|
module.exports = {
|
|
402
463
|
loadScene,
|
|
403
464
|
saveScene,
|
|
@@ -408,5 +469,7 @@ module.exports = {
|
|
|
408
469
|
reorderArrayToMatchChildren,
|
|
409
470
|
refreshEditor,
|
|
410
471
|
installPlugin,
|
|
411
|
-
checkPluginStatus
|
|
472
|
+
checkPluginStatus,
|
|
473
|
+
loadScriptMap,
|
|
474
|
+
buildTree
|
|
412
475
|
};
|