cocos2d-cli 1.1.1 → 1.2.1
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 +48 -32
- package/package.json +1 -1
- package/src/commands/create-scene.js +381 -403
- package/src/commands/prefab-create.js +449 -25
|
@@ -1,21 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* create-scene 命令 -
|
|
2
|
+
* create-scene 命令 - 从 JSON 结构创建场景文件
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
4
|
+
* JSON 格式示例:
|
|
5
|
+
* {
|
|
6
|
+
* "name": "Panel",
|
|
7
|
+
* "width": 400,
|
|
8
|
+
* "height": 300,
|
|
9
|
+
* "color": "#ffffff",
|
|
10
|
+
* "components": [
|
|
11
|
+
* { "type": "sprite", "sizeMode": 1 },
|
|
12
|
+
* { "type": "widget", "top": 0, "bottom": 0 }
|
|
13
|
+
* ],
|
|
14
|
+
* "children": [...]
|
|
15
|
+
* }
|
|
16
|
+
*
|
|
17
|
+
* 节点属性:name, width, height, x, y, color, opacity, anchorX, anchorY, rotation, scaleX, scaleY, active
|
|
18
|
+
* 组件属性:type + 各组件特有属性
|
|
19
|
+
*
|
|
20
|
+
* 场景会自动包含 Canvas 和 Main Camera 节点
|
|
12
21
|
*/
|
|
13
22
|
|
|
14
23
|
const fs = require('fs');
|
|
15
24
|
const path = require('path');
|
|
16
|
-
const { Components, generateId
|
|
25
|
+
const { Components, generateId } = require('../lib/components');
|
|
17
26
|
|
|
18
|
-
//
|
|
27
|
+
// 支持的组件类型
|
|
19
28
|
const COMPONENT_TYPES = {
|
|
20
29
|
'sprite': 'sprite',
|
|
21
30
|
'label': 'label',
|
|
@@ -28,262 +37,392 @@ const COMPONENT_TYPES = {
|
|
|
28
37
|
'particlesystem': 'particleSystem'
|
|
29
38
|
};
|
|
30
39
|
|
|
31
|
-
// 渲染组件(一个节点只能有一个)
|
|
32
|
-
const RENDER_COMPONENTS = ['sprite', 'label', 'graphics', 'mask', 'richtext', 'particleSystem'];
|
|
33
|
-
|
|
34
|
-
// 功能组件(可以和渲染组件共存,也可以多个共存)
|
|
35
|
-
const FUNCTIONAL_COMPONENTS = ['button', 'widget', 'layout', 'canvas', 'camera'];
|
|
36
|
-
|
|
37
40
|
/**
|
|
38
|
-
*
|
|
39
|
-
* @returns {object} { valid: boolean, error?: string, warning?: string }
|
|
41
|
+
* 解析颜色字符串 #RRGGBB 或 #RRGGBBAA
|
|
40
42
|
*/
|
|
41
|
-
function
|
|
42
|
-
|
|
43
|
+
function parseColor(colorStr) {
|
|
44
|
+
if (!colorStr || typeof colorStr !== 'string') return null;
|
|
43
45
|
|
|
44
|
-
|
|
46
|
+
let hex = colorStr.replace('#', '');
|
|
47
|
+
if (hex.length === 6) {
|
|
45
48
|
return {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
49
|
+
r: parseInt(hex.substring(0, 2), 16),
|
|
50
|
+
g: parseInt(hex.substring(2, 4), 16),
|
|
51
|
+
b: parseInt(hex.substring(4, 6), 16),
|
|
52
|
+
a: 255
|
|
53
|
+
};
|
|
54
|
+
} else if (hex.length === 8) {
|
|
55
|
+
return {
|
|
56
|
+
r: parseInt(hex.substring(0, 2), 16),
|
|
57
|
+
g: parseInt(hex.substring(2, 4), 16),
|
|
58
|
+
b: parseInt(hex.substring(4, 6), 16),
|
|
59
|
+
a: parseInt(hex.substring(6, 8), 16)
|
|
55
60
|
};
|
|
56
61
|
}
|
|
57
|
-
|
|
58
|
-
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 生成 UUID
|
|
67
|
+
*/
|
|
68
|
+
function generateUUID() {
|
|
69
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
70
|
+
const r = Math.random() * 16 | 0;
|
|
71
|
+
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
|
72
|
+
return v.toString(16);
|
|
73
|
+
});
|
|
59
74
|
}
|
|
60
75
|
|
|
61
76
|
/**
|
|
62
|
-
*
|
|
77
|
+
* 解析组件定义
|
|
63
78
|
*/
|
|
64
|
-
function
|
|
65
|
-
|
|
79
|
+
function parseComponent(compDef) {
|
|
80
|
+
if (typeof compDef === 'string') {
|
|
81
|
+
const type = COMPONENT_TYPES[compDef.toLowerCase()];
|
|
82
|
+
return type ? { type, props: {} } : null;
|
|
83
|
+
}
|
|
66
84
|
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (!validation.valid) {
|
|
72
|
-
errors.push({ node: nodePath, error: validation.error });
|
|
73
|
-
}
|
|
85
|
+
if (typeof compDef === 'object' && compDef.type) {
|
|
86
|
+
const type = COMPONENT_TYPES[compDef.type.toLowerCase()];
|
|
87
|
+
if (!type) return null;
|
|
74
88
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
errors.push(...childErrors);
|
|
79
|
-
}
|
|
89
|
+
const props = { ...compDef };
|
|
90
|
+
delete props.type;
|
|
91
|
+
return { type, props };
|
|
80
92
|
}
|
|
81
93
|
|
|
82
|
-
return
|
|
94
|
+
return null;
|
|
83
95
|
}
|
|
84
96
|
|
|
85
97
|
/**
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* - 节点名称 #width=100 #height=50 #x=10 #y=20
|
|
91
|
-
*
|
|
92
|
-
* 树形符号支持:
|
|
93
|
-
* ├─ └─ │ 以及 Windows 下可能出现的 ? 乱码形式
|
|
98
|
+
* 应用组件属性
|
|
99
|
+
* @param comp 组件对象
|
|
100
|
+
* @param props 属性对象
|
|
101
|
+
* @param node 节点对象(可选,用于 label 的 color 设置到节点)
|
|
94
102
|
*/
|
|
95
|
-
function
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
// 策略:计算 ?? 或 ├─ 这样的分支标记数量
|
|
106
|
-
|
|
107
|
-
let depth = 0;
|
|
108
|
-
let contentStart = 0;
|
|
109
|
-
|
|
110
|
-
// 首先尝试匹配树形模式
|
|
111
|
-
// 模式1: Unicode 树形符号 ├ └
|
|
112
|
-
// 模式2: Windows 乱码 ??
|
|
113
|
-
const branchPattern = /([├└]─|├|└|\?\?|\?)\s*/g;
|
|
114
|
-
const branches = [];
|
|
115
|
-
let match;
|
|
116
|
-
let lastBranchEnd = 0;
|
|
117
|
-
|
|
118
|
-
while ((match = branchPattern.exec(line)) !== null) {
|
|
119
|
-
branches.push(match[1]);
|
|
120
|
-
lastBranchEnd = match.index + match[0].length;
|
|
103
|
+
function applyComponentProps(comp, props, node) {
|
|
104
|
+
if (!props || !comp) return;
|
|
105
|
+
|
|
106
|
+
const type = comp.__type__;
|
|
107
|
+
|
|
108
|
+
// Label 的 color 属性应该设置到节点上
|
|
109
|
+
if (type === 'cc.Label' && props.color && node) {
|
|
110
|
+
const parsed = parseColor(props.color);
|
|
111
|
+
if (parsed) {
|
|
112
|
+
node._color = { "__type__": "cc.Color", ...parsed };
|
|
121
113
|
}
|
|
114
|
+
delete props.color; // 不再在组件中处理
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Widget 特殊处理:根据设置的方向计算 alignFlags
|
|
118
|
+
if (type === 'cc.Widget') {
|
|
119
|
+
const ALIGN = {
|
|
120
|
+
top: 1,
|
|
121
|
+
verticalCenter: 2,
|
|
122
|
+
bottom: 4,
|
|
123
|
+
left: 8,
|
|
124
|
+
horizontalCenter: 16,
|
|
125
|
+
right: 32
|
|
126
|
+
};
|
|
127
|
+
let alignFlags = 0;
|
|
122
128
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
contentStart = lastBranchEnd;
|
|
127
|
-
} else {
|
|
128
|
-
// 没有分支符号,检查缩进
|
|
129
|
-
for (let i = 0; i < line.length; i++) {
|
|
130
|
-
const char = line[i];
|
|
131
|
-
if (char !== ' ' && char !== '\t') {
|
|
132
|
-
contentStart = i;
|
|
133
|
-
break;
|
|
134
|
-
}
|
|
129
|
+
for (const dir of ['top', 'bottom', 'left', 'right', 'horizontalCenter', 'verticalCenter']) {
|
|
130
|
+
if (props[dir] !== undefined && props[dir] !== null) {
|
|
131
|
+
alignFlags |= ALIGN[dir];
|
|
135
132
|
}
|
|
136
|
-
depth = Math.floor(contentStart / 4);
|
|
137
133
|
}
|
|
138
|
-
|
|
139
|
-
// 提取节点内容
|
|
140
|
-
let content = line.substring(contentStart).trim();
|
|
141
134
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
if (!content) continue;
|
|
146
|
-
|
|
147
|
-
// 解析节点信息
|
|
148
|
-
const nodeInfo = parseNodeLine(content);
|
|
149
|
-
|
|
150
|
-
// 构建树结构
|
|
151
|
-
while (stack.length > depth) {
|
|
152
|
-
stack.pop();
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const node = {
|
|
156
|
-
name: nodeInfo.name,
|
|
157
|
-
components: nodeInfo.components,
|
|
158
|
-
options: nodeInfo.options,
|
|
159
|
-
children: []
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
if (stack.length === 0) {
|
|
163
|
-
rootNodes.push(node);
|
|
164
|
-
} else {
|
|
165
|
-
stack[stack.length - 1].node.children.push(node);
|
|
135
|
+
if (alignFlags > 0) {
|
|
136
|
+
comp._alignFlags = alignFlags;
|
|
166
137
|
}
|
|
167
|
-
|
|
168
|
-
stack.push({ depth, node });
|
|
169
138
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
139
|
+
|
|
140
|
+
// 通用属性映射
|
|
141
|
+
const propMap = {
|
|
142
|
+
'sizeMode': '_sizeMode',
|
|
143
|
+
'fillType': '_fillType',
|
|
144
|
+
'fillCenter': '_fillCenter',
|
|
145
|
+
'fillStart': '_fillStart',
|
|
146
|
+
'fillRange': '_fillRange',
|
|
147
|
+
'trim': '_isTrimmedMode',
|
|
148
|
+
'string': '_string',
|
|
149
|
+
'fontSize': '_fontSize',
|
|
150
|
+
'lineHeight': '_lineHeight',
|
|
151
|
+
'horizontalAlign': '_N$horizontalAlign',
|
|
152
|
+
'verticalAlign': '_N$verticalAlign',
|
|
153
|
+
'overflow': '_N$overflow',
|
|
154
|
+
'fontFamily': '_N$fontFamily',
|
|
155
|
+
'wrap': '_enableWrapText',
|
|
156
|
+
'alignFlags': '_alignFlags',
|
|
157
|
+
'left': '_left',
|
|
158
|
+
'right': '_right',
|
|
159
|
+
'top': '_top',
|
|
160
|
+
'bottom': '_bottom',
|
|
161
|
+
'horizontalCenter': '_horizontalCenter',
|
|
162
|
+
'verticalCenter': '_verticalCenter',
|
|
163
|
+
'isAbsLeft': '_isAbsLeft',
|
|
164
|
+
'isAbsRight': '_isAbsRight',
|
|
165
|
+
'isAbsTop': '_isAbsTop',
|
|
166
|
+
'isAbsBottom': '_isAbsBottom',
|
|
167
|
+
'interactable': '_N$interactable',
|
|
168
|
+
'transition': '_N$transition',
|
|
169
|
+
'zoomScale': 'zoomScale',
|
|
170
|
+
'duration': 'duration',
|
|
171
|
+
'layoutType': '_N$layoutType',
|
|
172
|
+
'cellSize': '_N$cellSize',
|
|
173
|
+
'startAxis': '_N$startAxis',
|
|
174
|
+
'paddingLeft': '_N$paddingLeft',
|
|
175
|
+
'paddingRight': '_N$paddingRight',
|
|
176
|
+
'paddingTop': '_N$paddingTop',
|
|
177
|
+
'paddingBottom': '_N$paddingBottom',
|
|
178
|
+
'spacingX': '_N$spacingX',
|
|
179
|
+
'spacingY': '_N$spacingY',
|
|
180
|
+
'resize': '_resize',
|
|
181
|
+
'designResolution': '_designResolution',
|
|
182
|
+
'fitWidth': '_fitWidth',
|
|
183
|
+
'fitHeight': '_fitHeight',
|
|
184
|
+
'orthoSize': '_orthoSize',
|
|
185
|
+
'backgroundColor': '_backgroundColor',
|
|
186
|
+
'cullingMask': '_cullingMask',
|
|
187
|
+
'zoomRatio': '_zoomRatio'
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
if (type === 'cc.Label' && props.string !== undefined) {
|
|
191
|
+
comp._N$string = props.string;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
for (const [key, value] of Object.entries(props)) {
|
|
195
|
+
const compKey = propMap[key];
|
|
196
|
+
if (compKey) {
|
|
197
|
+
if (key === 'fillCenter' && Array.isArray(value)) {
|
|
198
|
+
comp[compKey] = { "__type__": "cc.Vec2", "x": value[0], "y": value[1] };
|
|
199
|
+
} else if (key === 'cellSize' && Array.isArray(value)) {
|
|
200
|
+
comp[compKey] = { "__type__": "cc.Size", "width": value[0], "height": value[1] };
|
|
201
|
+
} else if (key === 'designResolution' && Array.isArray(value)) {
|
|
202
|
+
comp[compKey] = { "__type__": "cc.Size", "width": value[0], "height": value[1] };
|
|
203
|
+
} else if ((key === 'backgroundColor' || key === 'color') && typeof value === 'string') {
|
|
204
|
+
const color = parseColor(value);
|
|
205
|
+
if (color) {
|
|
206
|
+
comp[compKey] = { "__type__": "cc.Color", ...color };
|
|
207
|
+
}
|
|
208
|
+
} else {
|
|
209
|
+
comp[compKey] = value;
|
|
191
210
|
}
|
|
192
211
|
}
|
|
193
212
|
}
|
|
194
|
-
|
|
195
|
-
// 提取选项 #key=value
|
|
196
|
-
const optionMatches = name.matchAll(/#(\w+)=([^\s#]+)/g);
|
|
197
|
-
for (const match of optionMatches) {
|
|
198
|
-
const key = match[1];
|
|
199
|
-
let value = match[2];
|
|
200
|
-
|
|
201
|
-
// 类型转换
|
|
202
|
-
if (/^\d+$/.test(value)) value = parseInt(value);
|
|
203
|
-
else if (/^\d+\.\d+$/.test(value)) value = parseFloat(value);
|
|
204
|
-
else if (value === 'true') value = true;
|
|
205
|
-
else if (value === 'false') value = false;
|
|
206
|
-
|
|
207
|
-
options[key] = value;
|
|
208
|
-
}
|
|
209
|
-
name = name.replace(/#\w+=[^\s#]+/g, '').trim();
|
|
210
|
-
|
|
211
|
-
return { name, components, options };
|
|
212
213
|
}
|
|
213
214
|
|
|
214
215
|
/**
|
|
215
|
-
*
|
|
216
|
+
* 创建场景数据结构
|
|
216
217
|
*/
|
|
217
|
-
function
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
218
|
+
function createSceneData(nodeDefs, sceneName) {
|
|
219
|
+
const data = [];
|
|
220
|
+
|
|
221
|
+
// 场景 UUID
|
|
222
|
+
const sceneUUID = generateUUID();
|
|
223
|
+
const canvasUUID = generateUUID();
|
|
224
|
+
const cameraUUID = generateUUID();
|
|
225
|
+
|
|
226
|
+
// 索引 0: cc.SceneAsset
|
|
227
|
+
data.push({
|
|
228
|
+
"__type__": "cc.SceneAsset",
|
|
229
|
+
"_name": sceneName || "NewScene",
|
|
230
|
+
"_objFlags": 0,
|
|
231
|
+
"_native": "",
|
|
232
|
+
"scene": { "__id__": 1 }
|
|
222
233
|
});
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* 创建场景文件数据结构(基于模板)
|
|
227
|
-
*/
|
|
228
|
-
function createSceneData(rootNodes, sceneName) {
|
|
229
|
-
// 加载模板
|
|
230
|
-
const templatePath = path.join(__dirname, '..', '..', 'data', 'scene-template.json');
|
|
231
|
-
let data;
|
|
232
234
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
235
|
+
// 索引 1: cc.Scene
|
|
236
|
+
data.push({
|
|
237
|
+
"__type__": "cc.Scene",
|
|
238
|
+
"_name": sceneName || "NewScene",
|
|
239
|
+
"_objFlags": 0,
|
|
240
|
+
"_parent": null,
|
|
241
|
+
"_children": [{ "__id__": 2 }],
|
|
242
|
+
"_active": true,
|
|
243
|
+
"_components": [],
|
|
244
|
+
"_prefab": null,
|
|
245
|
+
"_opacity": 255,
|
|
246
|
+
"_color": { "__type__": "cc.Color", "r": 255, "g": 255, "b": 255, "a": 255 },
|
|
247
|
+
"_contentSize": { "__type__": "cc.Size", "width": 0, "height": 0 },
|
|
248
|
+
"_anchorPoint": { "__type__": "cc.Vec2", "x": 0, "y": 0 },
|
|
249
|
+
"_trs": { "__type__": "TypedArray", "ctor": "Float64Array", "array": [0, 0, 0, 0, 0, 0, 1, 1, 1, 1] },
|
|
250
|
+
"_is3DNode": true,
|
|
251
|
+
"_groupIndex": 0,
|
|
252
|
+
"groupIndex": 0,
|
|
253
|
+
"autoReleaseAssets": false,
|
|
254
|
+
"_id": sceneUUID
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
// 索引 2: Canvas 节点
|
|
258
|
+
data.push({
|
|
259
|
+
"__type__": "cc.Node",
|
|
260
|
+
"_name": "Canvas",
|
|
261
|
+
"_objFlags": 0,
|
|
262
|
+
"_parent": { "__id__": 1 },
|
|
263
|
+
"_children": [{ "__id__": 3 }], // Main Camera
|
|
264
|
+
"_active": true,
|
|
265
|
+
"_components": [{ "__id__": 5 }, { "__id__": 6 }], // Canvas, Widget
|
|
266
|
+
"_prefab": null,
|
|
267
|
+
"_opacity": 255,
|
|
268
|
+
"_color": { "__type__": "cc.Color", "r": 255, "g": 255, "b": 255, "a": 255 },
|
|
269
|
+
"_contentSize": { "__type__": "cc.Size", "width": 960, "height": 640 },
|
|
270
|
+
"_anchorPoint": { "__type__": "cc.Vec2", "x": 0.5, "y": 0.5 },
|
|
271
|
+
"_trs": { "__type__": "TypedArray", "ctor": "Float64Array", "array": [480, 320, 0, 0, 0, 0, 1, 1, 1, 1] },
|
|
272
|
+
"_eulerAngles": { "__type__": "cc.Vec3", "x": 0, "y": 0, "z": 0 },
|
|
273
|
+
"_skewX": 0,
|
|
274
|
+
"_skewY": 0,
|
|
275
|
+
"_is3DNode": false,
|
|
276
|
+
"_groupIndex": 0,
|
|
277
|
+
"groupIndex": 0,
|
|
278
|
+
"_id": canvasUUID
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// 索引 3: Main Camera 节点
|
|
282
|
+
data.push({
|
|
283
|
+
"__type__": "cc.Node",
|
|
284
|
+
"_name": "Main Camera",
|
|
285
|
+
"_objFlags": 0,
|
|
286
|
+
"_parent": { "__id__": 2 },
|
|
287
|
+
"_children": [],
|
|
288
|
+
"_active": true,
|
|
289
|
+
"_components": [{ "__id__": 4 }], // Camera
|
|
290
|
+
"_prefab": null,
|
|
291
|
+
"_opacity": 255,
|
|
292
|
+
"_color": { "__type__": "cc.Color", "r": 255, "g": 255, "b": 255, "a": 255 },
|
|
293
|
+
"_contentSize": { "__type__": "cc.Size", "width": 0, "height": 0 },
|
|
294
|
+
"_anchorPoint": { "__type__": "cc.Vec2", "x": 0.5, "y": 0.5 },
|
|
295
|
+
"_trs": { "__type__": "TypedArray", "ctor": "Float64Array", "array": [0, 0, 0, 0, 0, 0, 1, 1, 1, 1] },
|
|
296
|
+
"_eulerAngles": { "__type__": "cc.Vec3", "x": 0, "y": 0, "z": 0 },
|
|
297
|
+
"_skewX": 0,
|
|
298
|
+
"_skewY": 0,
|
|
299
|
+
"_is3DNode": false,
|
|
300
|
+
"_groupIndex": 0,
|
|
301
|
+
"groupIndex": 0,
|
|
302
|
+
"_id": cameraUUID
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
// 索引 4: Camera 组件
|
|
306
|
+
data.push({
|
|
307
|
+
"__type__": "cc.Camera",
|
|
308
|
+
"_name": "",
|
|
309
|
+
"_objFlags": 0,
|
|
310
|
+
"node": { "__id__": 3 },
|
|
311
|
+
"_enabled": true,
|
|
312
|
+
"_cullingMask": 4294967295,
|
|
313
|
+
"_clearFlags": 7,
|
|
314
|
+
"_backgroundColor": { "__type__": "cc.Color", "r": 0, "g": 0, "b": 0, "a": 255 },
|
|
315
|
+
"_depth": -1,
|
|
316
|
+
"_zoomRatio": 1,
|
|
317
|
+
"_targetTexture": null,
|
|
318
|
+
"_fov": 60,
|
|
319
|
+
"_orthoSize": 10,
|
|
320
|
+
"_nearClip": 1,
|
|
321
|
+
"_farClip": 4096,
|
|
322
|
+
"_ortho": true,
|
|
323
|
+
"_rect": { "__type__": "cc.Rect", "x": 0, "y": 0, "width": 1, "height": 1 },
|
|
324
|
+
"_renderStages": 1,
|
|
325
|
+
"_alignWithScreen": true,
|
|
326
|
+
"_id": generateId()
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// 索引 5: Canvas 组件
|
|
330
|
+
data.push({
|
|
331
|
+
"__type__": "cc.Canvas",
|
|
332
|
+
"_name": "",
|
|
333
|
+
"_objFlags": 0,
|
|
334
|
+
"node": { "__id__": 2 },
|
|
335
|
+
"_enabled": true,
|
|
336
|
+
"_designResolution": { "__type__": "cc.Size", "width": 960, "height": 640 },
|
|
337
|
+
"_fitWidth": false,
|
|
338
|
+
"_fitHeight": true,
|
|
339
|
+
"_id": generateId()
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
// 索引 6: Widget 组件 (Canvas)
|
|
343
|
+
data.push({
|
|
344
|
+
"__type__": "cc.Widget",
|
|
345
|
+
"_name": "",
|
|
346
|
+
"_objFlags": 0,
|
|
347
|
+
"node": { "__id__": 2 },
|
|
348
|
+
"_enabled": true,
|
|
349
|
+
"alignMode": 1,
|
|
350
|
+
"_target": null,
|
|
351
|
+
"_alignFlags": 45,
|
|
352
|
+
"_left": 0,
|
|
353
|
+
"_right": 0,
|
|
354
|
+
"_top": 0,
|
|
355
|
+
"_bottom": 0,
|
|
356
|
+
"_verticalCenter": 0,
|
|
357
|
+
"_horizontalCenter": 0,
|
|
358
|
+
"_isAbsLeft": true,
|
|
359
|
+
"_isAbsRight": true,
|
|
360
|
+
"_isAbsTop": true,
|
|
361
|
+
"_isAbsBottom": true,
|
|
362
|
+
"_isAbsHorizontalCenter": true,
|
|
363
|
+
"_isAbsVerticalCenter": true,
|
|
364
|
+
"_originalWidth": 0,
|
|
365
|
+
"_originalHeight": 0,
|
|
366
|
+
"_id": generateId()
|
|
367
|
+
});
|
|
249
368
|
|
|
250
|
-
// Canvas
|
|
369
|
+
// Canvas 节点索引
|
|
251
370
|
const canvasIndex = 2;
|
|
252
|
-
const canvas = data[canvasIndex];
|
|
253
371
|
|
|
254
|
-
//
|
|
255
|
-
function addNode(
|
|
372
|
+
// 递归添加用户节点
|
|
373
|
+
function addNode(def, parentIndex) {
|
|
256
374
|
const nodeIndex = data.length;
|
|
257
375
|
const uuid = generateUUID();
|
|
258
376
|
|
|
377
|
+
// 解析组件
|
|
378
|
+
const compList = (def.components || [])
|
|
379
|
+
.map(parseComponent)
|
|
380
|
+
.filter(Boolean);
|
|
381
|
+
|
|
382
|
+
// 解析颜色
|
|
383
|
+
let color = { "__type__": "cc.Color", "r": 255, "g": 255, "b": 255, "a": 255 };
|
|
384
|
+
if (def.color) {
|
|
385
|
+
const parsed = parseColor(def.color);
|
|
386
|
+
if (parsed) {
|
|
387
|
+
color = { "__type__": "cc.Color", ...parsed };
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// 解析尺寸和位置
|
|
392
|
+
const width = def.width || 0;
|
|
393
|
+
const height = def.height || 0;
|
|
394
|
+
const anchorX = def.anchorX !== undefined ? def.anchorX : 0.5;
|
|
395
|
+
const anchorY = def.anchorY !== undefined ? def.anchorY : 0.5;
|
|
396
|
+
const rotation = def.rotation || 0;
|
|
397
|
+
const scaleX = def.scaleX !== undefined ? def.scaleX : 1;
|
|
398
|
+
const scaleY = def.scaleY !== undefined ? def.scaleY : 1;
|
|
399
|
+
|
|
259
400
|
// 创建节点
|
|
260
401
|
const node = {
|
|
261
402
|
"__type__": "cc.Node",
|
|
262
|
-
"_name":
|
|
403
|
+
"_name": def.name || 'Node',
|
|
263
404
|
"_objFlags": 0,
|
|
264
405
|
"_parent": { "__id__": parentIndex },
|
|
265
406
|
"_children": [],
|
|
266
|
-
"_active":
|
|
407
|
+
"_active": def.active !== false,
|
|
267
408
|
"_components": [],
|
|
268
409
|
"_prefab": null,
|
|
269
|
-
"_opacity": 255,
|
|
270
|
-
"_color":
|
|
271
|
-
"_contentSize": {
|
|
272
|
-
|
|
273
|
-
"width": nodeDef.options.width || 0,
|
|
274
|
-
"height": nodeDef.options.height || 0
|
|
275
|
-
},
|
|
276
|
-
"_anchorPoint": { "__type__": "cc.Vec2", "x": 0.5, "y": 0.5 },
|
|
410
|
+
"_opacity": def.opacity !== undefined ? def.opacity : 255,
|
|
411
|
+
"_color": color,
|
|
412
|
+
"_contentSize": { "__type__": "cc.Size", width, height },
|
|
413
|
+
"_anchorPoint": { "__type__": "cc.Vec2", "x": anchorX, "y": anchorY },
|
|
277
414
|
"_trs": {
|
|
278
415
|
"__type__": "TypedArray",
|
|
279
416
|
"ctor": "Float64Array",
|
|
280
417
|
"array": [
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
0, 0, 0,
|
|
418
|
+
def.x || 0,
|
|
419
|
+
def.y || 0,
|
|
420
|
+
0, 0, 0,
|
|
421
|
+
rotation * Math.PI / 180,
|
|
422
|
+
1, scaleX, scaleY, 1
|
|
284
423
|
]
|
|
285
424
|
},
|
|
286
|
-
"_eulerAngles": { "__type__": "cc.Vec3", "x": 0, "y": 0, "z":
|
|
425
|
+
"_eulerAngles": { "__type__": "cc.Vec3", "x": 0, "y": 0, "z": rotation },
|
|
287
426
|
"_skewX": 0,
|
|
288
427
|
"_skewY": 0,
|
|
289
428
|
"_is3DNode": false,
|
|
@@ -295,9 +434,10 @@ function createSceneData(rootNodes, sceneName) {
|
|
|
295
434
|
data.push(node);
|
|
296
435
|
|
|
297
436
|
// 添加组件
|
|
298
|
-
for (const
|
|
299
|
-
if (Components[
|
|
300
|
-
const comp = Components[
|
|
437
|
+
for (const { type, props } of compList) {
|
|
438
|
+
if (Components[type]) {
|
|
439
|
+
const comp = Components[type](nodeIndex);
|
|
440
|
+
applyComponentProps(comp, props, node);
|
|
301
441
|
const compIndex = data.length;
|
|
302
442
|
data.push(comp);
|
|
303
443
|
node._components.push({ "__id__": compIndex });
|
|
@@ -305,223 +445,63 @@ function createSceneData(rootNodes, sceneName) {
|
|
|
305
445
|
}
|
|
306
446
|
|
|
307
447
|
// 更新父节点的 _children
|
|
308
|
-
|
|
309
|
-
if (parent && parent._children) {
|
|
310
|
-
parent._children.push({ "__id__": nodeIndex });
|
|
311
|
-
}
|
|
448
|
+
data[parentIndex]._children.push({ "__id__": nodeIndex });
|
|
312
449
|
|
|
313
450
|
// 递归处理子节点
|
|
314
|
-
|
|
315
|
-
|
|
451
|
+
if (def.children && def.children.length > 0) {
|
|
452
|
+
for (const child of def.children) {
|
|
453
|
+
addNode(child, nodeIndex);
|
|
454
|
+
}
|
|
316
455
|
}
|
|
317
456
|
|
|
318
457
|
return nodeIndex;
|
|
319
458
|
}
|
|
320
459
|
|
|
321
|
-
//
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
}
|
|
328
|
-
} else {
|
|
329
|
-
// 用户定义的是其他节点,直接添加到 Canvas 下
|
|
330
|
-
for (const rootNode of rootNodes) {
|
|
331
|
-
addNode(rootNode, canvasIndex);
|
|
332
|
-
}
|
|
460
|
+
// 支持数组或单个节点
|
|
461
|
+
const nodes = Array.isArray(nodeDefs) ? nodeDefs : [nodeDefs];
|
|
462
|
+
|
|
463
|
+
// 添加用户节点到 Canvas
|
|
464
|
+
for (const nodeDef of nodes) {
|
|
465
|
+
addNode(nodeDef, canvasIndex);
|
|
333
466
|
}
|
|
334
467
|
|
|
335
468
|
return data;
|
|
336
469
|
}
|
|
337
470
|
|
|
338
|
-
/**
|
|
339
|
-
* 创建基础场景模板(当模板文件不存在时使用)
|
|
340
|
-
*/
|
|
341
|
-
function createBasicSceneTemplate() {
|
|
342
|
-
return [
|
|
343
|
-
{
|
|
344
|
-
"__type__": "cc.SceneAsset",
|
|
345
|
-
"_name": "",
|
|
346
|
-
"_objFlags": 0,
|
|
347
|
-
"_native": "",
|
|
348
|
-
"scene": { "__id__": 1 }
|
|
349
|
-
},
|
|
350
|
-
{
|
|
351
|
-
"__type__": "cc.Scene",
|
|
352
|
-
"_objFlags": 0,
|
|
353
|
-
"_parent": null,
|
|
354
|
-
"_children": [{ "__id__": 2 }],
|
|
355
|
-
"_active": true,
|
|
356
|
-
"_components": [],
|
|
357
|
-
"_prefab": null,
|
|
358
|
-
"_opacity": 255,
|
|
359
|
-
"_color": { "__type__": "cc.Color", "r": 255, "g": 255, "b": 255, "a": 255 },
|
|
360
|
-
"_contentSize": { "__type__": "cc.Size", "width": 0, "height": 0 },
|
|
361
|
-
"_anchorPoint": { "__type__": "cc.Vec2", "x": 0, "y": 0 },
|
|
362
|
-
"_trs": { "__type__": "TypedArray", "ctor": "Float64Array", "array": [0, 0, 0, 0, 0, 0, 1, 1, 1, 1] },
|
|
363
|
-
"_is3DNode": true,
|
|
364
|
-
"_groupIndex": 0,
|
|
365
|
-
"groupIndex": 0,
|
|
366
|
-
"autoReleaseAssets": false,
|
|
367
|
-
"_id": ""
|
|
368
|
-
},
|
|
369
|
-
{
|
|
370
|
-
"__type__": "cc.Node",
|
|
371
|
-
"_name": "Canvas",
|
|
372
|
-
"_objFlags": 0,
|
|
373
|
-
"_parent": { "__id__": 1 },
|
|
374
|
-
"_children": [{ "__id__": 3 }],
|
|
375
|
-
"_active": true,
|
|
376
|
-
"_components": [{ "__id__": 5 }, { "__id__": 6 }],
|
|
377
|
-
"_prefab": null,
|
|
378
|
-
"_opacity": 255,
|
|
379
|
-
"_color": { "__type__": "cc.Color", "r": 255, "g": 255, "b": 255, "a": 255 },
|
|
380
|
-
"_contentSize": { "__type__": "cc.Size", "width": 960, "height": 640 },
|
|
381
|
-
"_anchorPoint": { "__type__": "cc.Vec2", "x": 0.5, "y": 0.5 },
|
|
382
|
-
"_trs": { "__type__": "TypedArray", "ctor": "Float64Array", "array": [480, 320, 0, 0, 0, 0, 1, 1, 1, 1] },
|
|
383
|
-
"_eulerAngles": { "__type__": "cc.Vec3", "x": 0, "y": 0, "z": 0 },
|
|
384
|
-
"_skewX": 0,
|
|
385
|
-
"_skewY": 0,
|
|
386
|
-
"_is3DNode": false,
|
|
387
|
-
"_groupIndex": 0,
|
|
388
|
-
"groupIndex": 0,
|
|
389
|
-
"_id": ""
|
|
390
|
-
},
|
|
391
|
-
{
|
|
392
|
-
"__type__": "cc.Node",
|
|
393
|
-
"_name": "Main Camera",
|
|
394
|
-
"_objFlags": 0,
|
|
395
|
-
"_parent": { "__id__": 2 },
|
|
396
|
-
"_children": [],
|
|
397
|
-
"_active": true,
|
|
398
|
-
"_components": [{ "__id__": 4 }],
|
|
399
|
-
"_prefab": null,
|
|
400
|
-
"_opacity": 255,
|
|
401
|
-
"_color": { "__type__": "cc.Color", "r": 255, "g": 255, "b": 255, "a": 255 },
|
|
402
|
-
"_contentSize": { "__type__": "cc.Size", "width": 0, "height": 0 },
|
|
403
|
-
"_anchorPoint": { "__type__": "cc.Vec2", "x": 0.5, "y": 0.5 },
|
|
404
|
-
"_trs": { "__type__": "TypedArray", "ctor": "Float64Array", "array": [0, 0, 0, 0, 0, 0, 1, 1, 1, 1] },
|
|
405
|
-
"_eulerAngles": { "__type__": "cc.Vec3", "x": 0, "y": 0, "z": 0 },
|
|
406
|
-
"_skewX": 0,
|
|
407
|
-
"_skewY": 0,
|
|
408
|
-
"_is3DNode": false,
|
|
409
|
-
"_groupIndex": 0,
|
|
410
|
-
"groupIndex": 0,
|
|
411
|
-
"_id": ""
|
|
412
|
-
},
|
|
413
|
-
{
|
|
414
|
-
"__type__": "cc.Camera",
|
|
415
|
-
"_name": "",
|
|
416
|
-
"_objFlags": 0,
|
|
417
|
-
"node": { "__id__": 3 },
|
|
418
|
-
"_enabled": true,
|
|
419
|
-
"_cullingMask": 4294967295,
|
|
420
|
-
"_clearFlags": 7,
|
|
421
|
-
"_backgroundColor": { "__type__": "cc.Color", "r": 0, "g": 0, "b": 0, "a": 255 },
|
|
422
|
-
"_depth": -1,
|
|
423
|
-
"_zoomRatio": 1,
|
|
424
|
-
"_targetTexture": null,
|
|
425
|
-
"_fov": 60,
|
|
426
|
-
"_orthoSize": 10,
|
|
427
|
-
"_nearClip": 1,
|
|
428
|
-
"_farClip": 4096,
|
|
429
|
-
"_ortho": true,
|
|
430
|
-
"_rect": { "__type__": "cc.Rect", "x": 0, "y": 0, "width": 1, "height": 1 },
|
|
431
|
-
"_renderStages": 1,
|
|
432
|
-
"_alignWithScreen": true,
|
|
433
|
-
"_id": ""
|
|
434
|
-
},
|
|
435
|
-
{
|
|
436
|
-
"__type__": "cc.Canvas",
|
|
437
|
-
"_name": "",
|
|
438
|
-
"_objFlags": 0,
|
|
439
|
-
"node": { "__id__": 2 },
|
|
440
|
-
"_enabled": true,
|
|
441
|
-
"_designResolution": { "__type__": "cc.Size", "width": 960, "height": 640 },
|
|
442
|
-
"_fitWidth": false,
|
|
443
|
-
"_fitHeight": true,
|
|
444
|
-
"_id": ""
|
|
445
|
-
},
|
|
446
|
-
{
|
|
447
|
-
"__type__": "cc.Widget",
|
|
448
|
-
"_name": "",
|
|
449
|
-
"_objFlags": 0,
|
|
450
|
-
"node": { "__id__": 2 },
|
|
451
|
-
"_enabled": true,
|
|
452
|
-
"alignMode": 1,
|
|
453
|
-
"_target": null,
|
|
454
|
-
"_alignFlags": 45,
|
|
455
|
-
"_left": 0,
|
|
456
|
-
"_right": 0,
|
|
457
|
-
"_top": 0,
|
|
458
|
-
"_bottom": 0,
|
|
459
|
-
"_verticalCenter": 0,
|
|
460
|
-
"_horizontalCenter": 0,
|
|
461
|
-
"_isAbsLeft": true,
|
|
462
|
-
"_isAbsRight": true,
|
|
463
|
-
"_isAbsTop": true,
|
|
464
|
-
"_isAbsBottom": true,
|
|
465
|
-
"_isAbsHorizontalCenter": true,
|
|
466
|
-
"_isAbsVerticalCenter": true,
|
|
467
|
-
"_originalWidth": 0,
|
|
468
|
-
"_originalHeight": 0,
|
|
469
|
-
"_id": ""
|
|
470
|
-
}
|
|
471
|
-
];
|
|
472
|
-
}
|
|
473
|
-
|
|
474
471
|
function run(args) {
|
|
475
472
|
if (args.length < 1) {
|
|
476
473
|
console.log(JSON.stringify({
|
|
477
|
-
error: '用法:
|
|
478
|
-
hint: '从 stdin
|
|
474
|
+
error: '用法: cocos2d-cli create-scene <输出路径.fire>',
|
|
475
|
+
hint: '从 stdin 读取 JSON 结构生成场景',
|
|
476
|
+
example: 'type scene.json | cocos2d-cli create-scene assets/scene.fire'
|
|
479
477
|
}));
|
|
480
478
|
return;
|
|
481
479
|
}
|
|
482
480
|
|
|
483
481
|
const outputPath = args[0];
|
|
484
|
-
const sceneName =
|
|
482
|
+
const sceneName = path.basename(outputPath, '.fire');
|
|
485
483
|
|
|
486
|
-
// 从 stdin
|
|
484
|
+
// 从 stdin 读取 JSON
|
|
487
485
|
let input = '';
|
|
488
486
|
|
|
489
|
-
// 检查是否是管道输入
|
|
490
487
|
if (!process.stdin.isTTY) {
|
|
491
|
-
// 同步读取 stdin(简化处理)
|
|
492
|
-
const fs = require('fs');
|
|
493
488
|
input = fs.readFileSync(0, 'utf8');
|
|
494
489
|
}
|
|
495
490
|
|
|
496
491
|
if (!input.trim()) {
|
|
497
492
|
console.log(JSON.stringify({
|
|
498
|
-
error: '请通过 stdin
|
|
499
|
-
example: `echo "Canvas (canvas)\\n├─ TopBar (sprite, widget)\\n│ └─ ScoreLabel (label)" | cocos2.4 create-scene assets/game.fire`
|
|
493
|
+
error: '请通过 stdin 提供 JSON 结构'
|
|
500
494
|
}));
|
|
501
495
|
return;
|
|
502
496
|
}
|
|
503
497
|
|
|
504
498
|
try {
|
|
505
|
-
//
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
if (rootNodes.length === 0) {
|
|
509
|
-
console.log(JSON.stringify({ error: '未能解析出任何节点' }));
|
|
510
|
-
return;
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
// 校验组件配置
|
|
514
|
-
const validationErrors = validateAllNodes(rootNodes);
|
|
515
|
-
if (validationErrors.length > 0) {
|
|
516
|
-
console.log(JSON.stringify({
|
|
517
|
-
error: '组件配置错误',
|
|
518
|
-
details: validationErrors
|
|
519
|
-
}));
|
|
520
|
-
return;
|
|
521
|
-
}
|
|
499
|
+
// 移除 BOM 并解析 JSON
|
|
500
|
+
input = input.replace(/^\uFEFF/, '').trim();
|
|
501
|
+
const nodeDef = JSON.parse(input);
|
|
522
502
|
|
|
523
503
|
// 生成场景数据
|
|
524
|
-
const sceneData = createSceneData(
|
|
504
|
+
const sceneData = createSceneData(nodeDef, sceneName);
|
|
525
505
|
|
|
526
506
|
// 确保输出目录存在
|
|
527
507
|
const outputDir = path.dirname(outputPath);
|
|
@@ -533,11 +513,10 @@ function run(args) {
|
|
|
533
513
|
fs.writeFileSync(outputPath, JSON.stringify(sceneData, null, 2), 'utf8');
|
|
534
514
|
|
|
535
515
|
// 统计信息
|
|
536
|
-
let nodeCount = 0;
|
|
537
|
-
let compCount = 0;
|
|
516
|
+
let nodeCount = 0, compCount = 0;
|
|
538
517
|
for (const item of sceneData) {
|
|
539
518
|
if (item.__type__ === 'cc.Node') nodeCount++;
|
|
540
|
-
else if (item.__type__?.startsWith('cc.') &&
|
|
519
|
+
else if (item.__type__?.startsWith('cc.') && !['cc.Scene', 'cc.SceneAsset'].includes(item.__type__)) {
|
|
541
520
|
compCount++;
|
|
542
521
|
}
|
|
543
522
|
}
|
|
@@ -546,8 +525,7 @@ function run(args) {
|
|
|
546
525
|
success: true,
|
|
547
526
|
path: outputPath,
|
|
548
527
|
nodes: nodeCount,
|
|
549
|
-
components: compCount
|
|
550
|
-
structure: rootNodes.map(n => n.name)
|
|
528
|
+
components: compCount
|
|
551
529
|
}));
|
|
552
530
|
|
|
553
531
|
} catch (err) {
|
|
@@ -555,4 +533,4 @@ function run(args) {
|
|
|
555
533
|
}
|
|
556
534
|
}
|
|
557
535
|
|
|
558
|
-
module.exports = { run };
|
|
536
|
+
module.exports = { run };
|