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
package/src/commands/add.js
CHANGED
|
@@ -1,14 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* add 命令 -
|
|
3
|
-
*
|
|
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 (根的,在最后)
|
|
4
16
|
*/
|
|
5
17
|
|
|
6
|
-
const { loadScene, saveScene, buildMaps,
|
|
18
|
+
const { loadScene, saveScene, buildMaps, refreshEditor, isPrefab, generateFileId } = require('../lib/fire-utils');
|
|
7
19
|
const { Components, createNodeData } = require('../lib/components');
|
|
8
20
|
|
|
9
21
|
/**
|
|
10
|
-
*
|
|
11
|
-
|
|
22
|
+
* 获取根节点 PrefabInfo 的索引(预制体最后一个元素)
|
|
23
|
+
*/
|
|
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;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return data.length - 1;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 获取节点子树的结束索引(子节点 → 组件 → PrefabInfo)
|
|
12
36
|
*/
|
|
13
37
|
function getSubtreeEndIndex(data, nodeIndex) {
|
|
14
38
|
const node = data[nodeIndex];
|
|
@@ -24,33 +48,35 @@ function getSubtreeEndIndex(data, nodeIndex) {
|
|
|
24
48
|
}
|
|
25
49
|
}
|
|
26
50
|
|
|
27
|
-
//
|
|
51
|
+
// 处理组件
|
|
28
52
|
if (node._components) {
|
|
29
53
|
for (const compRef of node._components) {
|
|
30
54
|
lastIndex = Math.max(lastIndex, compRef.__id__);
|
|
31
55
|
}
|
|
32
56
|
}
|
|
33
57
|
|
|
58
|
+
// PrefabInfo 在最后(根节点的除外,它在整个数组最后)
|
|
59
|
+
if (node._prefab && nodeIndex !== 1) {
|
|
60
|
+
lastIndex = Math.max(lastIndex, node._prefab.__id__);
|
|
61
|
+
}
|
|
62
|
+
|
|
34
63
|
return lastIndex;
|
|
35
64
|
}
|
|
36
65
|
|
|
37
66
|
/**
|
|
38
|
-
* 重建所有 __id__
|
|
39
|
-
* 返回新旧索引的映射表
|
|
67
|
+
* 重建所有 __id__ 引用
|
|
40
68
|
*/
|
|
41
|
-
function
|
|
69
|
+
function rebuildReferences(data, insertIndex, count) {
|
|
42
70
|
const indexMap = {};
|
|
43
71
|
|
|
44
|
-
// 构建映射:旧索引 -> 新索引
|
|
45
72
|
for (let oldIndex = 0; oldIndex < data.length; oldIndex++) {
|
|
46
73
|
if (oldIndex < insertIndex) {
|
|
47
74
|
indexMap[oldIndex] = oldIndex;
|
|
48
75
|
} else {
|
|
49
|
-
indexMap[oldIndex] = oldIndex +
|
|
76
|
+
indexMap[oldIndex] = oldIndex + count;
|
|
50
77
|
}
|
|
51
78
|
}
|
|
52
79
|
|
|
53
|
-
// 更新所有 __id__ 引用
|
|
54
80
|
function updateRef(obj) {
|
|
55
81
|
if (!obj || typeof obj !== 'object') return;
|
|
56
82
|
|
|
@@ -75,11 +101,11 @@ function rebuildReferencesForInsert(data, insertIndex) {
|
|
|
75
101
|
|
|
76
102
|
function run(args) {
|
|
77
103
|
if (args.length < 3) {
|
|
78
|
-
console.log(JSON.stringify({ error: '用法: cocos2.4 add
|
|
104
|
+
console.log(JSON.stringify({ error: '用法: cocos2.4 add <场景.fire | 预制体.prefab> <父节点索引> <节点名称> [选项]' }));
|
|
79
105
|
return;
|
|
80
106
|
}
|
|
81
107
|
|
|
82
|
-
const
|
|
108
|
+
const filePath = args[0];
|
|
83
109
|
const parentRef = args[1];
|
|
84
110
|
const nodeName = args[2];
|
|
85
111
|
|
|
@@ -95,133 +121,236 @@ function run(args) {
|
|
|
95
121
|
else if (key === 'height') options.height = parseFloat(value) || 0;
|
|
96
122
|
else if (key === 'at') options.at = parseInt(value);
|
|
97
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 || '';
|
|
98
127
|
}
|
|
99
128
|
});
|
|
100
129
|
|
|
101
130
|
try {
|
|
102
|
-
const data = loadScene(
|
|
103
|
-
const {
|
|
131
|
+
const data = loadScene(filePath);
|
|
132
|
+
const { prefab } = buildMaps(data);
|
|
104
133
|
|
|
105
|
-
|
|
106
|
-
|
|
134
|
+
if (!/^\d+$/.test(parentRef)) {
|
|
135
|
+
console.log(JSON.stringify({ error: '父节点必须使用数字索引,请先用 tree 命令查看节点索引' }));
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
107
138
|
|
|
108
|
-
|
|
109
|
-
|
|
139
|
+
const parentIndex = parseInt(parentRef);
|
|
140
|
+
|
|
141
|
+
if (parentIndex < 0 || parentIndex >= data.length || !data[parentIndex]) {
|
|
142
|
+
console.log(JSON.stringify({ error: `无效的节点索引: ${parentRef}` }));
|
|
110
143
|
return;
|
|
111
144
|
}
|
|
112
145
|
|
|
113
146
|
const parentNode = data[parentIndex];
|
|
147
|
+
const isRootChild = prefab && parentIndex === 1;
|
|
114
148
|
|
|
115
|
-
//
|
|
116
|
-
// 按照 _children 顺序,找到应该插入的位置
|
|
149
|
+
// 确定插入位置:紧跟父节点之后(在父节点的子节点/组件/PrefabInfo之前)
|
|
117
150
|
let insertIndex;
|
|
151
|
+
|
|
118
152
|
if (!parentNode._children || parentNode._children.length === 0) {
|
|
119
|
-
//
|
|
153
|
+
// 父节点还没有子节点,插入到父节点之后
|
|
120
154
|
insertIndex = parentIndex + 1;
|
|
121
155
|
} else {
|
|
122
|
-
//
|
|
156
|
+
// 父节点已有子节点,根据 --at 参数确定位置
|
|
123
157
|
const targetPosition = options.at >= 0 ? options.at : parentNode._children.length;
|
|
124
158
|
|
|
125
159
|
if (targetPosition === 0) {
|
|
126
|
-
//
|
|
127
|
-
insertIndex =
|
|
160
|
+
// 插入到第一个子节点之前
|
|
161
|
+
insertIndex = parentNode._children[0].__id__;
|
|
128
162
|
} else if (targetPosition >= parentNode._children.length) {
|
|
129
|
-
//
|
|
163
|
+
// 插入到最后一个子节点之后
|
|
130
164
|
const lastChildRef = parentNode._children[parentNode._children.length - 1];
|
|
131
165
|
insertIndex = getSubtreeEndIndex(data, lastChildRef.__id__) + 1;
|
|
132
166
|
} else {
|
|
133
|
-
//
|
|
134
|
-
|
|
135
|
-
insertIndex = getSubtreeEndIndex(data, beforeChildRef.__id__) + 1;
|
|
167
|
+
// 插入到中间
|
|
168
|
+
insertIndex = parentNode._children[targetPosition].__id__;
|
|
136
169
|
}
|
|
137
170
|
}
|
|
138
171
|
|
|
139
|
-
|
|
140
|
-
const newNode = createNodeData(nodeName, parentIndex, options);
|
|
141
|
-
|
|
142
|
-
// 在正确位置插入新节点
|
|
143
|
-
data.splice(insertIndex, 0, newNode);
|
|
144
|
-
|
|
145
|
-
// 重建索引引用(因为插入了新元素,后续索引都+1)
|
|
146
|
-
const insertIndexMap = rebuildReferencesForInsert(data, insertIndex);
|
|
147
|
-
|
|
148
|
-
// 新节点的实际索引
|
|
149
|
-
const newNodeIndex = insertIndex;
|
|
172
|
+
let newNodeIndex;
|
|
150
173
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const
|
|
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
|
+
}
|
|
219
|
+
}
|
|
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
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
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
|
+
|
|
159
309
|
if (compData) {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
data.splice(compInsertIndex, 0, compData);
|
|
163
|
-
|
|
164
|
-
// 再次重建引用
|
|
165
|
-
rebuildReferencesForInsert(data, compInsertIndex);
|
|
166
|
-
|
|
167
|
-
componentIndex = compInsertIndex;
|
|
168
|
-
newNode._components.push({ "__id__": componentIndex });
|
|
310
|
+
compData.node = { "__id__": newNodeIndex };
|
|
311
|
+
newNode._components.push({ "__id__": newNodeIndex + 1 });
|
|
169
312
|
}
|
|
170
313
|
}
|
|
171
314
|
|
|
172
315
|
// 更新父节点的 _children
|
|
173
316
|
if (!parentNode._children) parentNode._children = [];
|
|
174
|
-
|
|
175
317
|
const insertPosition = options.at >= 0 ? options.at : parentNode._children.length;
|
|
176
318
|
parentNode._children.splice(insertPosition, 0, { "__id__": newNodeIndex });
|
|
177
319
|
|
|
178
|
-
//
|
|
179
|
-
saveScene(
|
|
320
|
+
// 保存文件
|
|
321
|
+
saveScene(filePath, data);
|
|
180
322
|
|
|
181
|
-
//
|
|
182
|
-
refreshEditor(
|
|
183
|
-
|
|
184
|
-
// 构建节点树(类似 tree 命令)
|
|
185
|
-
function buildTree(nodeIndex, prefix = '', isLast = true, isRoot = true) {
|
|
186
|
-
const node = data[nodeIndex];
|
|
187
|
-
if (!node) return '';
|
|
188
|
-
|
|
189
|
-
const nodeName = isRoot ? 'Root' : (node._name || '(unnamed)');
|
|
190
|
-
const active = node._active !== false ? '●' : '○';
|
|
191
|
-
const connector = isRoot ? '' : (isLast ? '└── ' : '├── ');
|
|
192
|
-
|
|
193
|
-
let result = prefix + (isRoot ? '' : active + ' ') + nodeName + ' #' + nodeIndex;
|
|
194
|
-
|
|
195
|
-
// 添加组件信息
|
|
196
|
-
if (node._components && node._components.length > 0) {
|
|
197
|
-
const comps = node._components.map(c => {
|
|
198
|
-
const comp = data[c.__id__];
|
|
199
|
-
if (!comp) return `? #${c.__id__}`;
|
|
200
|
-
return `${comp.__type__.replace('cc.', '')} #${c.__id__}`;
|
|
201
|
-
}).join(', ');
|
|
202
|
-
result += ` (${comps})`;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
result += '\n';
|
|
206
|
-
|
|
207
|
-
// 处理子节点
|
|
208
|
-
if (node._children && node._children.length > 0) {
|
|
209
|
-
node._children.forEach((childRef, idx) => {
|
|
210
|
-
const childIsLast = idx === node._children.length - 1;
|
|
211
|
-
const childPrefix = prefix + (isRoot ? '' : (isLast ? ' ' : '│ '));
|
|
212
|
-
result += buildTree(childRef.__id__, childPrefix, childIsLast, false);
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return result;
|
|
217
|
-
}
|
|
323
|
+
// 触发编辑器刷新
|
|
324
|
+
refreshEditor(filePath);
|
|
218
325
|
|
|
219
|
-
|
|
326
|
+
console.log(JSON.stringify({
|
|
327
|
+
success: true,
|
|
328
|
+
nodeIndex: newNodeIndex,
|
|
329
|
+
name: nodeName,
|
|
330
|
+
parent: parentRef,
|
|
331
|
+
type: prefab ? 'prefab' : 'scene'
|
|
332
|
+
}));
|
|
220
333
|
|
|
221
|
-
console.log(treeStr);
|
|
222
334
|
} catch (err) {
|
|
223
335
|
console.log(JSON.stringify({ error: err.message }));
|
|
224
336
|
}
|
|
225
337
|
}
|
|
226
338
|
|
|
227
|
-
|
|
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
|
+
module.exports = { run };
|