cocos2d-cli 1.2.1 → 1.5.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.
Files changed (39) hide show
  1. package/README.md +142 -0
  2. package/bin/{cocos-cli.js → cocos2d-cli.js} +36 -22
  3. package/package.json +3 -3
  4. package/src/commands/add-component.js +75 -114
  5. package/src/commands/add.js +115 -294
  6. package/src/commands/build.js +1 -1
  7. package/src/commands/create-scene.js +133 -488
  8. package/src/commands/get.js +104 -158
  9. package/src/commands/prefab-create.js +57 -425
  10. package/src/commands/remove-component.js +81 -33
  11. package/src/commands/remove.js +66 -65
  12. package/src/commands/set.js +105 -253
  13. package/src/commands/tree.js +7 -11
  14. package/src/lib/cc/CCButton.js +122 -0
  15. package/src/lib/cc/CCCamera.js +93 -0
  16. package/src/lib/cc/CCCanvas.js +64 -0
  17. package/src/lib/cc/CCColor.js +32 -0
  18. package/src/lib/cc/CCComponent.js +60 -0
  19. package/src/lib/cc/CCLabel.js +109 -0
  20. package/src/lib/cc/CCNode.js +242 -0
  21. package/src/lib/cc/CCObject.js +23 -0
  22. package/src/lib/cc/CCPrefab.js +242 -0
  23. package/src/lib/cc/CCRect.js +32 -0
  24. package/src/lib/cc/CCScene.js +42 -0
  25. package/src/lib/cc/CCSceneAsset.js +271 -0
  26. package/src/lib/cc/CCSize.js +26 -0
  27. package/src/lib/cc/CCSprite.js +82 -0
  28. package/src/lib/cc/CCTrs.js +74 -0
  29. package/src/lib/cc/CCVec2.js +26 -0
  30. package/src/lib/cc/CCVec3.js +29 -0
  31. package/src/lib/cc/CCWidget.js +98 -0
  32. package/src/lib/cc/index.js +40 -0
  33. package/src/lib/fire-utils.js +58 -392
  34. package/src/lib/json-parser.js +166 -0
  35. package/src/lib/node-utils.js +395 -0
  36. package/src/lib/templates.js +49 -0
  37. package/src/lib/utils.js +202 -0
  38. package/src/commands/delete.js +0 -74
  39. package/src/lib/components.js +0 -404
@@ -1,356 +1,177 @@
1
1
  /**
2
- * add 命令 - 添加节点(支持场景和预制体)
3
- *
4
- * 预制体正确结构(深度优先遍历):
5
- * 节点 → 子节点(递归) → 组件 → PrefabInfo
6
- *
7
- * 示例:
8
- * [1] Node (根) _prefab -> [最后]
9
- * [2] Node (子1) _prefab -> [6]
10
- * [3] Node (孙) _prefab -> [5]
11
- * [4] Component (孙的组件)
12
- * [5] PrefabInfo (孙的)
13
- * [6] Component (子1的组件)
14
- * [7] PrefabInfo (子1的)
15
- * [8] PrefabInfo (根的,在最后)
2
+ * add 命令 - 添加节点
16
3
  */
17
4
 
18
- const { loadScene, saveScene, buildMaps, refreshEditor, isPrefab, generateFileId } = require('../lib/fire-utils');
19
- const { Components, createNodeData } = require('../lib/components');
5
+ const path = require('path');
6
+ const fs = require('fs');
7
+ const { CCNode, CCSceneAsset, CCPrefab, CCCanvas, CCWidget, CCSprite, CCLabel, CCButton } = require('../lib/cc');
8
+ const { buildTree } = require('../lib/node-utils');
9
+ const { loadScriptMap, isPrefab } = require('../lib/fire-utils');
10
+ const { generateCompressedUUID } = require('../lib/utils');
20
11
 
21
12
  /**
22
- * 获取根节点 PrefabInfo 的索引(预制体最后一个元素)
13
+ * 解析命令行选项
23
14
  */
24
- function getRootPrefabInfoIndex(data) {
25
- for (let i = data.length - 1; i >= 0; i--) {
26
- if (data[i].__type__ === 'cc.PrefabInfo') {
27
- const rootRef = data[i].root?.__id__;
28
- if (rootRef === 1) return i;
15
+ function parseOptions(args, startIndex) {
16
+ const options = {};
17
+ for (let i = startIndex; i < args.length; i++) {
18
+ const arg = args[i];
19
+ if (arg.startsWith('--')) {
20
+ const eqIndex = arg.indexOf('=');
21
+ if (eqIndex > 0) {
22
+ const key = arg.substring(2, eqIndex);
23
+ let value = arg.substring(eqIndex + 1);
24
+ if (!isNaN(value) && value !== '') {
25
+ value = parseFloat(value);
26
+ }
27
+ options[key] = value;
28
+ }
29
29
  }
30
30
  }
31
- return data.length - 1;
31
+ return options;
32
32
  }
33
33
 
34
34
  /**
35
- * 获取节点子树的结束索引(子节点 → 组件 → PrefabInfo)
35
+ * 创建组件
36
36
  */
37
- function getSubtreeEndIndex(data, nodeIndex) {
38
- const node = data[nodeIndex];
39
- if (!node) return nodeIndex;
40
-
41
- let lastIndex = nodeIndex;
42
-
43
- // 递归处理所有子节点
44
- if (node._children) {
45
- for (const childRef of node._children) {
46
- const childEnd = getSubtreeEndIndex(data, childRef.__id__);
47
- lastIndex = Math.max(lastIndex, childEnd);
48
- }
49
- }
50
-
51
- // 处理组件
52
- if (node._components) {
53
- for (const compRef of node._components) {
54
- lastIndex = Math.max(lastIndex, compRef.__id__);
55
- }
56
- }
57
-
58
- // PrefabInfo 在最后(根节点的除外,它在整个数组最后)
59
- if (node._prefab && nodeIndex !== 1) {
60
- lastIndex = Math.max(lastIndex, node._prefab.__id__);
37
+ function createComponent(type) {
38
+ switch (type.toLowerCase()) {
39
+ case 'canvas': return new CCCanvas();
40
+ case 'widget': return new CCWidget();
41
+ case 'sprite': return new CCSprite();
42
+ case 'label': return new CCLabel();
43
+ case 'button': return new CCButton();
44
+ default: return null;
61
45
  }
62
-
63
- return lastIndex;
64
46
  }
65
47
 
66
48
  /**
67
- * 重建所有 __id__ 引用
49
+ * 查找节点
68
50
  */
69
- function rebuildReferences(data, insertIndex, count) {
70
- const indexMap = {};
51
+ function findNode(root, path) {
52
+ if (!path) return root;
71
53
 
72
- for (let oldIndex = 0; oldIndex < data.length; oldIndex++) {
73
- if (oldIndex < insertIndex) {
74
- indexMap[oldIndex] = oldIndex;
75
- } else {
76
- indexMap[oldIndex] = oldIndex + count;
77
- }
78
- }
54
+ const parts = path.split('/').filter(p => p);
55
+ if (parts.length === 0) return root;
79
56
 
80
- function updateRef(obj) {
81
- if (!obj || typeof obj !== 'object') return;
82
-
83
- if (obj.__id__ !== undefined) {
84
- const oldId = obj.__id__;
85
- if (indexMap[oldId] !== undefined) {
86
- obj.__id__ = indexMap[oldId];
87
- }
88
- } else {
89
- for (const key of Object.keys(obj)) {
90
- updateRef(obj[key]);
91
- }
92
- }
57
+ let current = root;
58
+
59
+ // 如果路径以根节点名称开始,跳过
60
+ if (parts[0] === root._name) {
61
+ parts.shift();
93
62
  }
94
63
 
95
- for (const item of data) {
96
- updateRef(item);
64
+ for (const part of parts) {
65
+ if (!current._children || current._children.length === 0) return null;
66
+ const found = current._children.find(c => c._name === part);
67
+ if (!found) return null;
68
+ current = found;
97
69
  }
98
70
 
99
- return indexMap;
71
+ return current;
100
72
  }
101
73
 
102
74
  function run(args) {
103
75
  if (args.length < 3) {
104
- console.log(JSON.stringify({ error: '用法: cocos2.4 add <场景.fire | 预制体.prefab> <父节点索引> <节点名称> [选项]' }));
76
+ console.log(JSON.stringify({ error: '用法: cocos2d-cli add <场景.fire|预制体.prefab> <父节点路径> <节点名称> [--type=组件类型] [--x=N] [--y=N]' }));
105
77
  return;
106
78
  }
107
79
 
108
80
  const filePath = args[0];
109
- const parentRef = args[1];
81
+ const parentPath = args[1];
110
82
  const nodeName = args[2];
83
+ const options = parseOptions(args, 3);
111
84
 
112
- // 解析选项
113
- const options = {};
114
- args.slice(3).forEach(arg => {
115
- if (arg.startsWith('--')) {
116
- const [key, value] = arg.substring(2).split('=');
117
- if (key === 'type') options.type = value;
118
- else if (key === 'x') options.x = parseFloat(value) || 0;
119
- else if (key === 'y') options.y = parseFloat(value) || 0;
120
- else if (key === 'width') options.width = parseFloat(value) || 0;
121
- else if (key === 'height') options.height = parseFloat(value) || 0;
122
- else if (key === 'at') options.at = parseInt(value);
123
- else if (key === 'active') options.active = value !== 'false';
124
- else if (key === 'color') options.color = value;
125
- else if (key === 'fontSize') options.fontSize = parseInt(value) || 40;
126
- else if (key === 'string') options.string = value || '';
127
- }
128
- });
85
+ const ext = path.extname(filePath).toLowerCase();
129
86
 
130
87
  try {
131
- const data = loadScene(filePath);
132
- const { prefab } = buildMaps(data);
88
+ let root;
89
+ let asset;
90
+ const json = JSON.parse(fs.readFileSync(filePath, 'utf8'));
133
91
 
134
- if (!/^\d+$/.test(parentRef)) {
135
- console.log(JSON.stringify({ error: '父节点必须使用数字索引,请先用 tree 命令查看节点索引' }));
92
+ if (ext === '.fire') {
93
+ asset = CCSceneAsset.fromJSON(json);
94
+ root = asset._scene;
95
+ } else if (ext === '.prefab') {
96
+ asset = CCPrefab.fromJSON(json);
97
+ root = asset._root;
98
+ } else {
99
+ console.log(JSON.stringify({ error: '不支持的文件类型,仅支持 .fire 和 .prefab' }));
136
100
  return;
137
101
  }
138
102
 
139
- const parentIndex = parseInt(parentRef);
103
+ const parent = findNode(root, parentPath);
140
104
 
141
- if (parentIndex < 0 || parentIndex >= data.length || !data[parentIndex]) {
142
- console.log(JSON.stringify({ error: `无效的节点索引: ${parentRef}` }));
105
+ if (!parent) {
106
+ console.log(JSON.stringify({ error: `父节点不存在: ${parentPath}` }));
143
107
  return;
144
108
  }
145
109
 
146
- const parentNode = data[parentIndex];
147
- const isRootChild = prefab && parentIndex === 1;
148
-
149
- // 确定插入位置:紧跟父节点之后(在父节点的子节点/组件/PrefabInfo之前)
150
- let insertIndex;
110
+ // 创建节点
111
+ const node = new CCNode(nodeName);
151
112
 
152
- if (!parentNode._children || parentNode._children.length === 0) {
153
- // 父节点还没有子节点,插入到父节点之后
154
- insertIndex = parentIndex + 1;
155
- } else {
156
- // 父节点已有子节点,根据 --at 参数确定位置
157
- const targetPosition = options.at >= 0 ? options.at : parentNode._children.length;
158
-
159
- if (targetPosition === 0) {
160
- // 插入到第一个子节点之前
161
- insertIndex = parentNode._children[0].__id__;
162
- } else if (targetPosition >= parentNode._children.length) {
163
- // 插入到最后一个子节点之后
164
- const lastChildRef = parentNode._children[parentNode._children.length - 1];
165
- insertIndex = getSubtreeEndIndex(data, lastChildRef.__id__) + 1;
166
- } else {
167
- // 插入到中间
168
- insertIndex = parentNode._children[targetPosition].__id__;
169
- }
113
+ // 应用属性
114
+ if (options.x !== undefined) node._trs.array[0] = parseFloat(options.x);
115
+ if (options.y !== undefined) node._trs.array[1] = parseFloat(options.y);
116
+ if (options.width !== undefined) node._contentSize.width = parseFloat(options.width);
117
+ if (options.height !== undefined) node._contentSize.height = parseFloat(options.height);
118
+ if (options.scaleX !== undefined) node._trs.array[7] = parseFloat(options.scaleX);
119
+ if (options.scaleY !== undefined) node._trs.array[8] = parseFloat(options.scaleY);
120
+ if (options.rotation !== undefined) {
121
+ node._trs.array[5] = parseFloat(options.rotation) * Math.PI / 180;
122
+ node._eulerAngles.z = parseFloat(options.rotation);
123
+ }
124
+ if (options.active !== undefined) {
125
+ node._active = options.active !== 'false' && options.active !== false;
170
126
  }
171
127
 
172
- let newNodeIndex;
173
-
174
- if (prefab) {
175
- // 预制体模式
176
- // 找到根节点的 PrefabInfo
177
- const rootPrefabInfoOldIdx = getRootPrefabInfoIndex(data);
178
- const rootPrefabInfo = data[rootPrefabInfoOldIdx];
179
-
180
- // 创建节点
181
- const nodeData = {
182
- "__type__": "cc.Node",
183
- "_name": nodeName,
184
- "_objFlags": 0,
185
- "_parent": { "__id__": parentIndex },
186
- "_children": [],
187
- "_active": options.active !== false,
188
- "_components": [],
189
- "_prefab": null,
190
- "_opacity": 255,
191
- "_color": parseColor(options.color) || { "__type__": "cc.Color", "r": 255, "g": 255, "b": 255, "a": 255 },
192
- "_contentSize": { "__type__": "cc.Size", "width": options.width || 0, "height": options.height || 0 },
193
- "_anchorPoint": { "__type__": "cc.Vec2", "x": 0.5, "y": 0.5 },
194
- "_trs": { "__type__": "TypedArray", "ctor": "Float64Array", "array": [options.x || 0, options.y || 0, 0, 0, 0, 0, 1, 1, 1, 1] },
195
- "_eulerAngles": { "__type__": "cc.Vec3", "x": 0, "y": 0, "z": 0 },
196
- "_skewX": 0,
197
- "_skewY": 0,
198
- "_is3DNode": false,
199
- "_groupIndex": 0,
200
- "groupIndex": 0,
201
- "_id": ""
202
- };
203
-
204
- // 创建组件(如果有)
205
- let compData = null;
206
- if (options.type) {
207
- compData = Components[options.type]?.(insertIndex);
208
- // 修改组件属性
209
- if (compData) {
210
- if (options.type === 'label') {
211
- if (options.fontSize) {
212
- compData._fontSize = options.fontSize;
213
- compData._lineHeight = options.fontSize;
214
- }
215
- if (options.string) {
216
- compData._string = options.string;
217
- compData._N$string = options.string;
218
- }
128
+ // 添加组件
129
+ if (options.type) {
130
+ const comp = createComponent(options.type);
131
+ if (comp) {
132
+ comp.node = node;
133
+ node._components = [comp];
134
+
135
+ // 组件特殊属性
136
+ if (options.type.toLowerCase() === 'label') {
137
+ if (options.string) {
138
+ comp._string = options.string;
139
+ comp['_N$string'] = options.string;
219
140
  }
220
- }
221
- }
222
-
223
- // 创建 PrefabInfo
224
- const prefabInfo = {
225
- "__type__": "cc.PrefabInfo",
226
- "root": { "__id__": 1 },
227
- "asset": { "__id__": 0 },
228
- "fileId": generateFileId(),
229
- "sync": false
230
- };
231
-
232
- // 构建要插入的元素:节点 → 组件 → PrefabInfo
233
- const itemsToInsert = [nodeData];
234
- if (compData) itemsToInsert.push(compData);
235
- itemsToInsert.push(prefabInfo);
236
-
237
- // 如果是根节点的子节点,先移除根 PrefabInfo
238
- if (isRootChild) {
239
- // 如果插入位置在根 PrefabInfo 之后,需要调整
240
- if (insertIndex > rootPrefabInfoOldIdx) {
241
- insertIndex--;
242
- }
243
- data.splice(rootPrefabInfoOldIdx, 1);
244
- }
245
-
246
- // 插入元素
247
- for (let i = 0; i < itemsToInsert.length; i++) {
248
- data.splice(insertIndex + i, 0, itemsToInsert[i]);
249
- }
250
-
251
- // 重建引用
252
- rebuildReferences(data, insertIndex, itemsToInsert.length);
253
-
254
- newNodeIndex = insertIndex;
255
-
256
- // 设置引用
257
- if (compData) {
258
- compData.node = { "__id__": newNodeIndex };
259
- nodeData._components.push({ "__id__": newNodeIndex + 1 });
260
- nodeData._prefab = { "__id__": newNodeIndex + 2 };
261
- } else {
262
- nodeData._prefab = { "__id__": newNodeIndex + 1 };
263
- }
264
-
265
- // 如果是根节点的子节点,把根 PrefabInfo 添加到最后
266
- if (isRootChild) {
267
- data.push(rootPrefabInfo);
268
- data[1]._prefab = { "__id__": data.length - 1 };
269
- }
270
-
271
- } else {
272
- // 场景模式:节点 + 组件
273
- const newNode = createNodeData(nodeName, parentIndex, options);
274
-
275
- if (options.color) {
276
- const color = parseColor(options.color);
277
- if (color) newNode._color = color;
278
- }
279
-
280
- const itemsToInsert = [newNode];
281
-
282
- let compData = null;
283
- if (options.type) {
284
- compData = Components[options.type]?.(insertIndex);
285
- // 修改组件属性
286
- if (compData) {
287
- if (options.type === 'label') {
288
- if (options.fontSize) {
289
- compData._fontSize = options.fontSize;
290
- compData._lineHeight = options.fontSize;
291
- }
292
- if (options.string) {
293
- compData._string = options.string;
294
- compData._N$string = options.string;
295
- }
141
+ if (options.fontSize) {
142
+ comp._fontSize = parseInt(options.fontSize);
143
+ comp._lineHeight = parseInt(options.fontSize);
296
144
  }
297
145
  }
298
- if (compData) itemsToInsert.push(compData);
299
- }
300
-
301
- for (let i = 0; i < itemsToInsert.length; i++) {
302
- data.splice(insertIndex + i, 0, itemsToInsert[i]);
303
- }
304
-
305
- rebuildReferences(data, insertIndex, itemsToInsert.length);
306
-
307
- newNodeIndex = insertIndex;
308
-
309
- if (compData) {
310
- compData.node = { "__id__": newNodeIndex };
311
- newNode._components.push({ "__id__": newNodeIndex + 1 });
312
146
  }
313
147
  }
314
148
 
315
- // 更新父节点的 _children
316
- if (!parentNode._children) parentNode._children = [];
317
- const insertPosition = options.at >= 0 ? options.at : parentNode._children.length;
318
- parentNode._children.splice(insertPosition, 0, { "__id__": newNodeIndex });
149
+ // 添加节点到父节点
150
+ parent._children = parent._children || [];
151
+ parent._children.push(node);
152
+ node._parent = parent;
319
153
 
320
- // 保存文件
321
- saveScene(filePath, data);
154
+ // 场景节点需要 _id,预制体需要 PrefabInfo
155
+ if (ext === '.fire') {
156
+ node._id = generateCompressedUUID();
157
+ } else if (ext === '.prefab') {
158
+ const { CCPrefabInfo } = require('../lib/cc');
159
+ node._prefab = new CCPrefabInfo();
160
+ }
322
161
 
323
- // 触发编辑器刷新
324
- refreshEditor(filePath);
162
+ // 保存
163
+ const data = asset.toJSON();
164
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf8');
325
165
 
326
- console.log(JSON.stringify({
327
- success: true,
328
- nodeIndex: newNodeIndex,
329
- name: nodeName,
330
- parent: parentRef,
331
- type: prefab ? 'prefab' : 'scene'
332
- }));
166
+ // 输出节点树
167
+ const scriptMap = loadScriptMap(filePath);
168
+ const prefab = isPrefab(data);
169
+ const startIndex = prefab ? 0 : 1;
170
+ console.log(buildTree(data, scriptMap, startIndex).trim());
333
171
 
334
172
  } catch (err) {
335
173
  console.log(JSON.stringify({ error: err.message }));
336
174
  }
337
175
  }
338
176
 
339
- function parseColor(colorStr) {
340
- if (!colorStr) return null;
341
- let color = colorStr;
342
- if (typeof color === 'string') {
343
- if (color.startsWith('#')) color = color.slice(1);
344
- if (color.length === 6) {
345
- const r = parseInt(color.slice(0, 2), 16);
346
- const g = parseInt(color.slice(2, 4), 16);
347
- const b = parseInt(color.slice(4, 6), 16);
348
- if (!isNaN(r) && !isNaN(g) && !isNaN(b)) {
349
- return { "__type__": "cc.Color", r, g, b, a: 255 };
350
- }
351
- }
352
- }
353
- return null;
354
- }
355
-
356
177
  module.exports = { run };
@@ -7,7 +7,7 @@ const path = require('path');
7
7
 
8
8
  function run(args) {
9
9
  if (args.length < 1) {
10
- console.log(JSON.stringify({ error: '用法: cocos-cli build <项目目录>' }));
10
+ console.log(JSON.stringify({ error: '用法: cocos2d-cli build <项目目录>' }));
11
11
  return;
12
12
  }
13
13