cocos2d-cli 1.0.4 → 1.1.0
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 +39 -21
- package/data/prefab-template.json +72 -0
- package/data/scene-template.json +241 -0
- package/package.json +4 -6
- package/src/commands/add.js +230 -101
- package/src/commands/create-scene.js +494 -0
- package/src/commands/delete.js +16 -15
- package/src/commands/get.js +153 -16
- package/src/commands/prefab-create.js +49 -0
- package/src/commands/remove.js +11 -76
- package/src/commands/set.js +145 -31
- package/src/commands/tree.js +15 -74
- package/src/lib/components.js +2 -2
- package/src/lib/fire-utils.js +233 -11
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* create-scene 命令 - 从树形文本结构创建场景文件
|
|
3
|
+
*
|
|
4
|
+
* 示例输入:
|
|
5
|
+
* Canvas
|
|
6
|
+
* ├─ TopBar (sprite, widget)
|
|
7
|
+
* │ ├─ ScoreLabel (label)
|
|
8
|
+
* │ ├─ LivesContainer
|
|
9
|
+
* │ └─ GoldLabel (label)
|
|
10
|
+
* ├─ GameArea
|
|
11
|
+
* └─ BottomBar
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const { Components, generateId, createNodeData } = require('../lib/components');
|
|
17
|
+
|
|
18
|
+
// 支持的组件类型映射
|
|
19
|
+
const COMPONENT_TYPES = {
|
|
20
|
+
'sprite': 'sprite',
|
|
21
|
+
'label': 'label',
|
|
22
|
+
'button': 'button',
|
|
23
|
+
'layout': 'layout',
|
|
24
|
+
'widget': 'widget',
|
|
25
|
+
'camera': 'camera',
|
|
26
|
+
'canvas': 'canvas',
|
|
27
|
+
'particle': 'particleSystem',
|
|
28
|
+
'particlesystem': 'particleSystem'
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 解析树形文本结构
|
|
33
|
+
* 支持格式:
|
|
34
|
+
* - 节点名称
|
|
35
|
+
* - 节点名称 (组件1, 组件2)
|
|
36
|
+
* - 节点名称 #width=100 #height=50 #x=10 #y=20
|
|
37
|
+
*
|
|
38
|
+
* 树形符号支持:
|
|
39
|
+
* ├─ └─ │ 以及 Windows 下可能出现的 ? 乱码形式
|
|
40
|
+
*/
|
|
41
|
+
function parseTreeText(text) {
|
|
42
|
+
const lines = text.split('\n').filter(line => line.trim());
|
|
43
|
+
const rootNodes = [];
|
|
44
|
+
const stack = []; // [{ depth, node }]
|
|
45
|
+
|
|
46
|
+
for (const line of lines) {
|
|
47
|
+
if (!line.trim()) continue;
|
|
48
|
+
|
|
49
|
+
// 计算深度:通过测量前导非内容字符
|
|
50
|
+
// Windows 下树形符号可能被编码成 ?? 或 ? ??
|
|
51
|
+
// 策略:计算 ?? 或 ├─ 这样的分支标记数量
|
|
52
|
+
|
|
53
|
+
let depth = 0;
|
|
54
|
+
let contentStart = 0;
|
|
55
|
+
|
|
56
|
+
// 首先尝试匹配树形模式
|
|
57
|
+
// 模式1: Unicode 树形符号 ├ └
|
|
58
|
+
// 模式2: Windows 乱码 ??
|
|
59
|
+
const branchPattern = /([├└]─|├|└|\?\?|\?)\s*/g;
|
|
60
|
+
const branches = [];
|
|
61
|
+
let match;
|
|
62
|
+
let lastBranchEnd = 0;
|
|
63
|
+
|
|
64
|
+
while ((match = branchPattern.exec(line)) !== null) {
|
|
65
|
+
branches.push(match[1]);
|
|
66
|
+
lastBranchEnd = match.index + match[0].length;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (branches.length > 0) {
|
|
70
|
+
// 找到了分支符号,深度 = 分支数量
|
|
71
|
+
depth = branches.length;
|
|
72
|
+
contentStart = lastBranchEnd;
|
|
73
|
+
} else {
|
|
74
|
+
// 没有分支符号,检查缩进
|
|
75
|
+
for (let i = 0; i < line.length; i++) {
|
|
76
|
+
const char = line[i];
|
|
77
|
+
if (char !== ' ' && char !== '\t') {
|
|
78
|
+
contentStart = i;
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
depth = Math.floor(contentStart / 4);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 提取节点内容
|
|
86
|
+
let content = line.substring(contentStart).trim();
|
|
87
|
+
|
|
88
|
+
// 清理可能残留的符号(- 和空格)
|
|
89
|
+
content = content.replace(/^[\-\s]+/, '').trim();
|
|
90
|
+
|
|
91
|
+
if (!content) continue;
|
|
92
|
+
|
|
93
|
+
// 解析节点信息
|
|
94
|
+
const nodeInfo = parseNodeLine(content);
|
|
95
|
+
|
|
96
|
+
// 构建树结构
|
|
97
|
+
while (stack.length > depth) {
|
|
98
|
+
stack.pop();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const node = {
|
|
102
|
+
name: nodeInfo.name,
|
|
103
|
+
components: nodeInfo.components,
|
|
104
|
+
options: nodeInfo.options,
|
|
105
|
+
children: []
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
if (stack.length === 0) {
|
|
109
|
+
rootNodes.push(node);
|
|
110
|
+
} else {
|
|
111
|
+
stack[stack.length - 1].node.children.push(node);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
stack.push({ depth, node });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return rootNodes;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* 解析单行节点定义
|
|
122
|
+
* 格式:NodeName (comp1, comp2) #key=value
|
|
123
|
+
*/
|
|
124
|
+
function parseNodeLine(line) {
|
|
125
|
+
let name = line;
|
|
126
|
+
let components = [];
|
|
127
|
+
let options = {};
|
|
128
|
+
|
|
129
|
+
// 提取组件 (xxx)
|
|
130
|
+
const compMatch = line.match(/\(([^)]+)\)/);
|
|
131
|
+
if (compMatch) {
|
|
132
|
+
name = name.replace(compMatch[0], '').trim();
|
|
133
|
+
const compList = compMatch[1].split(',').map(c => c.trim().toLowerCase());
|
|
134
|
+
for (const comp of compList) {
|
|
135
|
+
if (COMPONENT_TYPES[comp]) {
|
|
136
|
+
components.push(COMPONENT_TYPES[comp]);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// 提取选项 #key=value
|
|
142
|
+
const optionMatches = name.matchAll(/#(\w+)=([^\s#]+)/g);
|
|
143
|
+
for (const match of optionMatches) {
|
|
144
|
+
const key = match[1];
|
|
145
|
+
let value = match[2];
|
|
146
|
+
|
|
147
|
+
// 类型转换
|
|
148
|
+
if (/^\d+$/.test(value)) value = parseInt(value);
|
|
149
|
+
else if (/^\d+\.\d+$/.test(value)) value = parseFloat(value);
|
|
150
|
+
else if (value === 'true') value = true;
|
|
151
|
+
else if (value === 'false') value = false;
|
|
152
|
+
|
|
153
|
+
options[key] = value;
|
|
154
|
+
}
|
|
155
|
+
name = name.replace(/#\w+=[^\s#]+/g, '').trim();
|
|
156
|
+
|
|
157
|
+
return { name, components, options };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* 生成 UUID(简化版)
|
|
162
|
+
*/
|
|
163
|
+
function generateUUID() {
|
|
164
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
165
|
+
const r = Math.random() * 16 | 0;
|
|
166
|
+
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
|
167
|
+
return v.toString(16);
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* 创建场景文件数据结构(基于模板)
|
|
173
|
+
*/
|
|
174
|
+
function createSceneData(rootNodes, sceneName) {
|
|
175
|
+
// 加载模板
|
|
176
|
+
const templatePath = path.join(__dirname, '..', '..', 'data', 'scene-template.json');
|
|
177
|
+
let data;
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
const templateContent = fs.readFileSync(templatePath, 'utf8');
|
|
181
|
+
data = JSON.parse(templateContent);
|
|
182
|
+
} catch (e) {
|
|
183
|
+
// 如果模板加载失败,使用内置基础结构
|
|
184
|
+
data = createBasicSceneTemplate();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// 设置场景名称
|
|
188
|
+
data[0]._name = sceneName || "NewScene";
|
|
189
|
+
data[1]._name = sceneName || "NewScene";
|
|
190
|
+
|
|
191
|
+
// 生成新的 UUID
|
|
192
|
+
data[1]._id = generateUUID();
|
|
193
|
+
data[2]._id = generateUUID();
|
|
194
|
+
data[3]._id = generateUUID();
|
|
195
|
+
|
|
196
|
+
// Canvas 节点在索引 2
|
|
197
|
+
const canvasIndex = 2;
|
|
198
|
+
const canvas = data[canvasIndex];
|
|
199
|
+
|
|
200
|
+
// 递归添加节点
|
|
201
|
+
function addNode(nodeDef, parentIndex) {
|
|
202
|
+
const nodeIndex = data.length;
|
|
203
|
+
const uuid = generateUUID();
|
|
204
|
+
|
|
205
|
+
// 创建节点
|
|
206
|
+
const node = {
|
|
207
|
+
"__type__": "cc.Node",
|
|
208
|
+
"_name": nodeDef.name,
|
|
209
|
+
"_objFlags": 0,
|
|
210
|
+
"_parent": { "__id__": parentIndex },
|
|
211
|
+
"_children": [],
|
|
212
|
+
"_active": true,
|
|
213
|
+
"_components": [],
|
|
214
|
+
"_prefab": null,
|
|
215
|
+
"_opacity": 255,
|
|
216
|
+
"_color": { "__type__": "cc.Color", "r": 255, "g": 255, "b": 255, "a": 255 },
|
|
217
|
+
"_contentSize": {
|
|
218
|
+
"__type__": "cc.Size",
|
|
219
|
+
"width": nodeDef.options.width || 0,
|
|
220
|
+
"height": nodeDef.options.height || 0
|
|
221
|
+
},
|
|
222
|
+
"_anchorPoint": { "__type__": "cc.Vec2", "x": 0.5, "y": 0.5 },
|
|
223
|
+
"_trs": {
|
|
224
|
+
"__type__": "TypedArray",
|
|
225
|
+
"ctor": "Float64Array",
|
|
226
|
+
"array": [
|
|
227
|
+
nodeDef.options.x || 0,
|
|
228
|
+
nodeDef.options.y || 0,
|
|
229
|
+
0, 0, 0, 0, 1, 1, 1, 1
|
|
230
|
+
]
|
|
231
|
+
},
|
|
232
|
+
"_eulerAngles": { "__type__": "cc.Vec3", "x": 0, "y": 0, "z": 0 },
|
|
233
|
+
"_skewX": 0,
|
|
234
|
+
"_skewY": 0,
|
|
235
|
+
"_is3DNode": false,
|
|
236
|
+
"_groupIndex": 0,
|
|
237
|
+
"groupIndex": 0,
|
|
238
|
+
"_id": uuid
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
data.push(node);
|
|
242
|
+
|
|
243
|
+
// 添加组件
|
|
244
|
+
for (const compType of nodeDef.components) {
|
|
245
|
+
if (Components[compType]) {
|
|
246
|
+
const comp = Components[compType](nodeIndex);
|
|
247
|
+
const compIndex = data.length;
|
|
248
|
+
data.push(comp);
|
|
249
|
+
node._components.push({ "__id__": compIndex });
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// 更新父节点的 _children
|
|
254
|
+
const parent = data[parentIndex];
|
|
255
|
+
if (parent && parent._children) {
|
|
256
|
+
parent._children.push({ "__id__": nodeIndex });
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// 递归处理子节点
|
|
260
|
+
for (const child of nodeDef.children) {
|
|
261
|
+
addNode(child, nodeIndex);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return nodeIndex;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// 处理输入的节点结构
|
|
268
|
+
// 如果第一个节点是 Canvas,把它的子节点添加到模板的 Canvas 下
|
|
269
|
+
if (rootNodes.length > 0 && rootNodes[0].name === 'Canvas') {
|
|
270
|
+
// 用户定义了 Canvas 节点,把它的子节点添加到模板的 Canvas
|
|
271
|
+
for (const child of rootNodes[0].children) {
|
|
272
|
+
addNode(child, canvasIndex);
|
|
273
|
+
}
|
|
274
|
+
} else {
|
|
275
|
+
// 用户定义的是其他节点,直接添加到 Canvas 下
|
|
276
|
+
for (const rootNode of rootNodes) {
|
|
277
|
+
addNode(rootNode, canvasIndex);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return data;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* 创建基础场景模板(当模板文件不存在时使用)
|
|
286
|
+
*/
|
|
287
|
+
function createBasicSceneTemplate() {
|
|
288
|
+
return [
|
|
289
|
+
{
|
|
290
|
+
"__type__": "cc.SceneAsset",
|
|
291
|
+
"_name": "",
|
|
292
|
+
"_objFlags": 0,
|
|
293
|
+
"_native": "",
|
|
294
|
+
"scene": { "__id__": 1 }
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
"__type__": "cc.Scene",
|
|
298
|
+
"_objFlags": 0,
|
|
299
|
+
"_parent": null,
|
|
300
|
+
"_children": [{ "__id__": 2 }],
|
|
301
|
+
"_active": true,
|
|
302
|
+
"_components": [],
|
|
303
|
+
"_prefab": null,
|
|
304
|
+
"_opacity": 255,
|
|
305
|
+
"_color": { "__type__": "cc.Color", "r": 255, "g": 255, "b": 255, "a": 255 },
|
|
306
|
+
"_contentSize": { "__type__": "cc.Size", "width": 0, "height": 0 },
|
|
307
|
+
"_anchorPoint": { "__type__": "cc.Vec2", "x": 0, "y": 0 },
|
|
308
|
+
"_trs": { "__type__": "TypedArray", "ctor": "Float64Array", "array": [0, 0, 0, 0, 0, 0, 1, 1, 1, 1] },
|
|
309
|
+
"_is3DNode": true,
|
|
310
|
+
"_groupIndex": 0,
|
|
311
|
+
"groupIndex": 0,
|
|
312
|
+
"autoReleaseAssets": false,
|
|
313
|
+
"_id": ""
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
"__type__": "cc.Node",
|
|
317
|
+
"_name": "Canvas",
|
|
318
|
+
"_objFlags": 0,
|
|
319
|
+
"_parent": { "__id__": 1 },
|
|
320
|
+
"_children": [{ "__id__": 3 }],
|
|
321
|
+
"_active": true,
|
|
322
|
+
"_components": [{ "__id__": 5 }, { "__id__": 6 }],
|
|
323
|
+
"_prefab": null,
|
|
324
|
+
"_opacity": 255,
|
|
325
|
+
"_color": { "__type__": "cc.Color", "r": 255, "g": 255, "b": 255, "a": 255 },
|
|
326
|
+
"_contentSize": { "__type__": "cc.Size", "width": 960, "height": 640 },
|
|
327
|
+
"_anchorPoint": { "__type__": "cc.Vec2", "x": 0.5, "y": 0.5 },
|
|
328
|
+
"_trs": { "__type__": "TypedArray", "ctor": "Float64Array", "array": [480, 320, 0, 0, 0, 0, 1, 1, 1, 1] },
|
|
329
|
+
"_eulerAngles": { "__type__": "cc.Vec3", "x": 0, "y": 0, "z": 0 },
|
|
330
|
+
"_skewX": 0,
|
|
331
|
+
"_skewY": 0,
|
|
332
|
+
"_is3DNode": false,
|
|
333
|
+
"_groupIndex": 0,
|
|
334
|
+
"groupIndex": 0,
|
|
335
|
+
"_id": ""
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
"__type__": "cc.Node",
|
|
339
|
+
"_name": "Main Camera",
|
|
340
|
+
"_objFlags": 0,
|
|
341
|
+
"_parent": { "__id__": 2 },
|
|
342
|
+
"_children": [],
|
|
343
|
+
"_active": true,
|
|
344
|
+
"_components": [{ "__id__": 4 }],
|
|
345
|
+
"_prefab": null,
|
|
346
|
+
"_opacity": 255,
|
|
347
|
+
"_color": { "__type__": "cc.Color", "r": 255, "g": 255, "b": 255, "a": 255 },
|
|
348
|
+
"_contentSize": { "__type__": "cc.Size", "width": 0, "height": 0 },
|
|
349
|
+
"_anchorPoint": { "__type__": "cc.Vec2", "x": 0.5, "y": 0.5 },
|
|
350
|
+
"_trs": { "__type__": "TypedArray", "ctor": "Float64Array", "array": [0, 0, 0, 0, 0, 0, 1, 1, 1, 1] },
|
|
351
|
+
"_eulerAngles": { "__type__": "cc.Vec3", "x": 0, "y": 0, "z": 0 },
|
|
352
|
+
"_skewX": 0,
|
|
353
|
+
"_skewY": 0,
|
|
354
|
+
"_is3DNode": false,
|
|
355
|
+
"_groupIndex": 0,
|
|
356
|
+
"groupIndex": 0,
|
|
357
|
+
"_id": ""
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
"__type__": "cc.Camera",
|
|
361
|
+
"_name": "",
|
|
362
|
+
"_objFlags": 0,
|
|
363
|
+
"node": { "__id__": 3 },
|
|
364
|
+
"_enabled": true,
|
|
365
|
+
"_cullingMask": 4294967295,
|
|
366
|
+
"_clearFlags": 7,
|
|
367
|
+
"_backgroundColor": { "__type__": "cc.Color", "r": 0, "g": 0, "b": 0, "a": 255 },
|
|
368
|
+
"_depth": -1,
|
|
369
|
+
"_zoomRatio": 1,
|
|
370
|
+
"_targetTexture": null,
|
|
371
|
+
"_fov": 60,
|
|
372
|
+
"_orthoSize": 10,
|
|
373
|
+
"_nearClip": 1,
|
|
374
|
+
"_farClip": 4096,
|
|
375
|
+
"_ortho": true,
|
|
376
|
+
"_rect": { "__type__": "cc.Rect", "x": 0, "y": 0, "width": 1, "height": 1 },
|
|
377
|
+
"_renderStages": 1,
|
|
378
|
+
"_alignWithScreen": true,
|
|
379
|
+
"_id": ""
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
"__type__": "cc.Canvas",
|
|
383
|
+
"_name": "",
|
|
384
|
+
"_objFlags": 0,
|
|
385
|
+
"node": { "__id__": 2 },
|
|
386
|
+
"_enabled": true,
|
|
387
|
+
"_designResolution": { "__type__": "cc.Size", "width": 960, "height": 640 },
|
|
388
|
+
"_fitWidth": false,
|
|
389
|
+
"_fitHeight": true,
|
|
390
|
+
"_id": ""
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
"__type__": "cc.Widget",
|
|
394
|
+
"_name": "",
|
|
395
|
+
"_objFlags": 0,
|
|
396
|
+
"node": { "__id__": 2 },
|
|
397
|
+
"_enabled": true,
|
|
398
|
+
"alignMode": 1,
|
|
399
|
+
"_target": null,
|
|
400
|
+
"_alignFlags": 45,
|
|
401
|
+
"_left": 0,
|
|
402
|
+
"_right": 0,
|
|
403
|
+
"_top": 0,
|
|
404
|
+
"_bottom": 0,
|
|
405
|
+
"_verticalCenter": 0,
|
|
406
|
+
"_horizontalCenter": 0,
|
|
407
|
+
"_isAbsLeft": true,
|
|
408
|
+
"_isAbsRight": true,
|
|
409
|
+
"_isAbsTop": true,
|
|
410
|
+
"_isAbsBottom": true,
|
|
411
|
+
"_isAbsHorizontalCenter": true,
|
|
412
|
+
"_isAbsVerticalCenter": true,
|
|
413
|
+
"_originalWidth": 0,
|
|
414
|
+
"_originalHeight": 0,
|
|
415
|
+
"_id": ""
|
|
416
|
+
}
|
|
417
|
+
];
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function run(args) {
|
|
421
|
+
if (args.length < 1) {
|
|
422
|
+
console.log(JSON.stringify({
|
|
423
|
+
error: '用法: cocos2.4 create-scene <输出路径.fire> [场景名称]',
|
|
424
|
+
hint: '从 stdin 读取树形结构,例如:\n echo "Canvas\\n├─ TopBar (sprite)" | cocos2.4 create-scene assets/scene.fire'
|
|
425
|
+
}));
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const outputPath = args[0];
|
|
430
|
+
const sceneName = args[1] || path.basename(outputPath, '.fire');
|
|
431
|
+
|
|
432
|
+
// 从 stdin 读取树形结构
|
|
433
|
+
let input = '';
|
|
434
|
+
|
|
435
|
+
// 检查是否是管道输入
|
|
436
|
+
if (!process.stdin.isTTY) {
|
|
437
|
+
// 同步读取 stdin(简化处理)
|
|
438
|
+
const fs = require('fs');
|
|
439
|
+
input = fs.readFileSync(0, 'utf8');
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (!input.trim()) {
|
|
443
|
+
console.log(JSON.stringify({
|
|
444
|
+
error: '请通过 stdin 提供场景结构',
|
|
445
|
+
example: `echo "Canvas (canvas)\\n├─ TopBar (sprite, widget)\\n│ └─ ScoreLabel (label)" | cocos2.4 create-scene assets/game.fire`
|
|
446
|
+
}));
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
try {
|
|
451
|
+
// 解析树形结构
|
|
452
|
+
const rootNodes = parseTreeText(input);
|
|
453
|
+
|
|
454
|
+
if (rootNodes.length === 0) {
|
|
455
|
+
console.log(JSON.stringify({ error: '未能解析出任何节点' }));
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// 生成场景数据
|
|
460
|
+
const sceneData = createSceneData(rootNodes, sceneName);
|
|
461
|
+
|
|
462
|
+
// 确保输出目录存在
|
|
463
|
+
const outputDir = path.dirname(outputPath);
|
|
464
|
+
if (!fs.existsSync(outputDir)) {
|
|
465
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// 保存文件
|
|
469
|
+
fs.writeFileSync(outputPath, JSON.stringify(sceneData, null, 2), 'utf8');
|
|
470
|
+
|
|
471
|
+
// 统计信息
|
|
472
|
+
let nodeCount = 0;
|
|
473
|
+
let compCount = 0;
|
|
474
|
+
for (const item of sceneData) {
|
|
475
|
+
if (item.__type__ === 'cc.Node') nodeCount++;
|
|
476
|
+
else if (item.__type__?.startsWith('cc.') && item.__type__ !== 'cc.Scene' && item.__type__ !== 'cc.SceneAsset') {
|
|
477
|
+
compCount++;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
console.log(JSON.stringify({
|
|
482
|
+
success: true,
|
|
483
|
+
path: outputPath,
|
|
484
|
+
nodes: nodeCount,
|
|
485
|
+
components: compCount,
|
|
486
|
+
structure: rootNodes.map(n => n.name)
|
|
487
|
+
}));
|
|
488
|
+
|
|
489
|
+
} catch (err) {
|
|
490
|
+
console.log(JSON.stringify({ error: err.message }));
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
module.exports = { run };
|
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
|
}
|