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.
@@ -1,91 +1,32 @@
1
1
  /**
2
- * tree 命令 - 查看节点树
2
+ * tree 命令 - 查看节点树(支持场景和预制体)
3
3
  */
4
4
 
5
- const { loadScene, buildMaps } = require('../lib/fire-utils');
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, isPrefab } = require('../lib/fire-utils');
20
6
 
21
7
  function run(args) {
22
- const scenePath = args[0];
8
+ const filePath = args[0];
23
9
 
24
- if (!scenePath) {
25
- console.log(JSON.stringify({ error: '用法: cocos2.4 tree <场景文件路径>' }));
10
+ if (!filePath) {
11
+ console.log(JSON.stringify({ error: '用法: cocos2.4 tree <场景.fire | 预制体.prefab>' }));
26
12
  return;
27
13
  }
28
14
 
29
15
  try {
30
- const data = loadScene(scenePath);
31
- const { indexMap } = buildMaps(data);
32
- const scriptMap = loadScriptMap(scenePath);
33
- const uuidRegex = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i;
16
+ const data = loadScene(filePath);
17
+ const scriptMap = loadScriptMap(filePath);
18
+ const prefab = isPrefab(data);
34
19
 
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;
20
+ // 输出文件类型
21
+ if (prefab) {
22
+ console.log(`📦 Prefab: ${data[1]._name || 'Root'}\n`);
23
+ } else {
24
+ console.log(`🎬 Scene\n`);
83
25
  }
84
26
 
85
- const treeStr = buildTree(1);
86
- console.log(treeStr);
27
+ console.log(buildTree(data, scriptMap, 1));
87
28
  } catch (err) {
88
- console.log(`Error: ${err.message}`);
29
+ console.log(JSON.stringify({ error: err.message }));
89
30
  }
90
31
  }
91
32
 
@@ -31,9 +31,9 @@ const Components = {
31
31
  "_materials": [DEFAULT_MATERIAL],
32
32
  "_srcBlendFactor": 770,
33
33
  "_dstBlendFactor": 771,
34
- "_spriteFrame": DEFAULT_SPRITE_FRAME,
34
+ "_spriteFrame": SPLASH_SPRITE_FRAME,
35
35
  "_type": 0,
36
- "_sizeMode": 1,
36
+ "_sizeMode": 0,
37
37
  "_fillType": 0,
38
38
  "_fillCenter": { "__type__": "cc.Vec2", "x": 0, "y": 0 },
39
39
  "_fillStart": 0,
@@ -1,17 +1,24 @@
1
1
  /**
2
- * Fire 文件工具模块
3
- * 提供直接读取和操作 .fire 场景文件的功能
2
+ * Fire/Prefab 文件工具模块
3
+ * 提供直接读取和操作 .fire 场景文件和 .prefab 预制体文件的功能
4
4
  */
5
5
 
6
6
  const fs = require('fs');
7
7
  const path = require('path');
8
8
 
9
9
  /**
10
- * 加载场景文件
10
+ * 检测是否为预制体文件
11
+ */
12
+ function isPrefab(data) {
13
+ return data[0]?.__type__ === 'cc.Prefab';
14
+ }
15
+
16
+ /**
17
+ * 加载场景/预制体文件
11
18
  */
12
19
  function loadScene(scenePath) {
13
20
  if (!fs.existsSync(scenePath)) {
14
- throw new Error(`场景文件不存在: ${scenePath}`);
21
+ throw new Error(`文件不存在: ${scenePath}`);
15
22
  }
16
23
 
17
24
  const content = fs.readFileSync(scenePath, 'utf8');
@@ -19,23 +26,29 @@ function loadScene(scenePath) {
19
26
  }
20
27
 
21
28
  /**
22
- * 保存场景文件
29
+ * 保存场景/预制体文件
23
30
  */
24
31
  function saveScene(scenePath, data) {
25
32
  fs.writeFileSync(scenePath, JSON.stringify(data, null, 2), 'utf8');
26
33
  }
27
34
 
28
35
  /**
29
- * 构建 ID 和索引映射
36
+ * 构建 ID 和索引映射(自动适配场景和预制体)
30
37
  */
31
38
  function buildMaps(data) {
32
39
  const idMap = {}; // _id -> index
33
- const indexMap = {}; // index -> { _id, name, path }
40
+ const indexMap = {}; // index -> { _id, name, path, type }
41
+ const prefab = isPrefab(data);
34
42
 
35
43
  function traverse(nodeIndex, parentPath = '') {
36
44
  const node = data[nodeIndex];
37
45
  if (!node) return;
38
46
 
47
+ // 跳过非节点类型
48
+ if (!node.__type__?.startsWith('cc.Node') && node.__type__ !== 'cc.Scene') {
49
+ return;
50
+ }
51
+
39
52
  const nodeId = node._id;
40
53
  if (nodeId) {
41
54
  idMap[nodeId] = nodeIndex;
@@ -59,12 +72,15 @@ function buildMaps(data) {
59
72
  }
60
73
  }
61
74
 
62
- // Scene 开始遍历(索引 1)
63
- if (data[1]) {
75
+ if (prefab) {
76
+ // 预制体:从索引 1(根节点)开始遍历
77
+ traverse(1);
78
+ } else {
79
+ // 场景:从索引 1(Scene)开始遍历
64
80
  traverse(1);
65
81
  }
66
82
 
67
- return { idMap, indexMap };
83
+ return { idMap, indexMap, prefab };
68
84
  }
69
85
 
70
86
  /**
@@ -398,6 +414,205 @@ function installPlugin(scenePath) {
398
414
  }
399
415
  }
400
416
 
417
+ /**
418
+ * 加载脚本映射(用于显示自定义脚本组件名称)
419
+ */
420
+ function loadScriptMap(scenePath) {
421
+ const projectPath = path.dirname(path.dirname(scenePath));
422
+ const mapPath = path.join(projectPath, 'data', 'script_map.json');
423
+ try {
424
+ if (fs.existsSync(mapPath)) {
425
+ return JSON.parse(fs.readFileSync(mapPath, 'utf-8'));
426
+ }
427
+ } catch (e) {}
428
+ return {};
429
+ }
430
+
431
+ /**
432
+ * 构建节点树输出(自动适配场景和预制体)
433
+ */
434
+ function buildTree(data, scriptMap, nodeIndex, prefix = '', isLast = true, isRoot = true) {
435
+ const node = data[nodeIndex];
436
+ if (!node) return '';
437
+
438
+ // 跳过 Scene 类型(场景根节点)
439
+ const isSceneRoot = node.__type__ === 'cc.Scene';
440
+ const nodeName = isRoot ? 'Root' : (node._name || '(unnamed)');
441
+ const active = node._active !== false ? '●' : '○';
442
+ const uuidRegex = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i;
443
+
444
+ let result = '';
445
+
446
+ // 场景根节点特殊处理
447
+ if (isSceneRoot) {
448
+ result = prefix + '🎬 Scene\n';
449
+ } else {
450
+ result = prefix + (isRoot ? '' : active + ' ') + nodeName + ' #' + nodeIndex;
451
+
452
+ // 添加组件信息
453
+ if (node._components && node._components.length > 0) {
454
+ const comps = node._components.map(c => {
455
+ const comp = data[c.__id__];
456
+ if (!comp) return `? #${c.__id__}`;
457
+ const typeName = comp.__type__;
458
+ let displayName;
459
+ if (uuidRegex.test(typeName)) {
460
+ const scriptInfo = scriptMap[typeName];
461
+ displayName = (scriptInfo && scriptInfo.name) ? scriptInfo.name : '⚠️MissingScript';
462
+ } else if (typeName === 'MissingScript') {
463
+ displayName = '⚠️MissingScript';
464
+ } else {
465
+ displayName = typeName.replace('cc.', '');
466
+ }
467
+ return `${displayName} #${c.__id__}`;
468
+ }).join(', ');
469
+ result += ` (${comps})`;
470
+ }
471
+
472
+ result += '\n';
473
+ }
474
+
475
+ // 处理子节点
476
+ if (node._children && node._children.length > 0) {
477
+ node._children.forEach((childRef, idx) => {
478
+ const childIsLast = idx === node._children.length - 1;
479
+ const childPrefix = prefix + (isSceneRoot ? '' : (isRoot ? '' : (isLast ? ' ' : '│ ')));
480
+ result += buildTree(data, scriptMap, childRef.__id__, childPrefix, childIsLast, isSceneRoot);
481
+ });
482
+ }
483
+
484
+ return result;
485
+ }
486
+
487
+ /**
488
+ * 生成 fileId(用于 PrefabInfo)
489
+ */
490
+ function generateFileId() {
491
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
492
+ let result = '';
493
+ for (let i = 0; i < 22; i++) {
494
+ result += chars.charAt(Math.floor(Math.random() * chars.length));
495
+ }
496
+ return result;
497
+ }
498
+
499
+ /**
500
+ * 创建新预制体
501
+ * @param {string} name - 预制体名称
502
+ * @returns {Array} - 预制体数据
503
+ *
504
+ * 预制体结构说明:
505
+ * [0] cc.Prefab - 预制体元数据
506
+ * [1] cc.Node - 根节点,_prefab 指向最后一个 PrefabInfo
507
+ * [2] cc.PrefabInfo - 根节点的 PrefabInfo(在最后)
508
+ *
509
+ * 当添加子节点时,结构变为:
510
+ * [0] cc.Prefab
511
+ * [1] cc.Node (根) _prefab -> [N]
512
+ * [2] cc.Node (子1) _prefab -> [3]
513
+ * [3] cc.PrefabInfo (子1的)
514
+ * ...
515
+ * [N] cc.PrefabInfo (根节点的,在最后)
516
+ */
517
+ function createPrefab(name) {
518
+ const fileId = generateFileId();
519
+ return [
520
+ {
521
+ "__type__": "cc.Prefab",
522
+ "_name": "",
523
+ "_objFlags": 0,
524
+ "_native": "",
525
+ "data": { "__id__": 1 },
526
+ "optimizationPolicy": 0,
527
+ "asyncLoadAssets": false,
528
+ "readonly": false
529
+ },
530
+ {
531
+ "__type__": "cc.Node",
532
+ "_name": name,
533
+ "_objFlags": 0,
534
+ "_parent": null,
535
+ "_children": [],
536
+ "_active": true,
537
+ "_components": [],
538
+ "_prefab": { "__id__": 2 }, // 指向最后的 PrefabInfo
539
+ "_opacity": 255,
540
+ "_color": { "__type__": "cc.Color", "r": 255, "g": 255, "b": 255, "a": 255 },
541
+ "_contentSize": { "__type__": "cc.Size", "width": 0, "height": 0 },
542
+ "_anchorPoint": { "__type__": "cc.Vec2", "x": 0.5, "y": 0.5 },
543
+ "_trs": { "__type__": "TypedArray", "ctor": "Float64Array", "array": [0, 0, 0, 0, 0, 0, 1, 1, 1, 1] },
544
+ "_eulerAngles": { "__type__": "cc.Vec3", "x": 0, "y": 0, "z": 0 },
545
+ "_skewX": 0,
546
+ "_skewY": 0,
547
+ "_is3DNode": false,
548
+ "_groupIndex": 0,
549
+ "groupIndex": 0,
550
+ "_id": ""
551
+ },
552
+ {
553
+ "__type__": "cc.PrefabInfo",
554
+ "root": { "__id__": 1 },
555
+ "asset": { "__id__": 0 },
556
+ "fileId": fileId,
557
+ "sync": false
558
+ }
559
+ ];
560
+ }
561
+
562
+ /**
563
+ * 创建预制体节点数据(带 PrefabInfo)
564
+ * @param {string} name - 节点名称
565
+ * @param {number} parentId - 父节点索引
566
+ * @param {number} rootId - 预制体根节点索引
567
+ * @param {object} options - 可选参数
568
+ */
569
+ function createPrefabNodeData(name, parentId, rootId, options = {}) {
570
+ const fileId = generateFileId();
571
+ const nodeIndex = -1; // 占位,插入时确定
572
+ const prefabInfoIndex = nodeIndex + 1; // PrefabInfo 紧跟节点
573
+
574
+ const nodeData = {
575
+ "__type__": "cc.Node",
576
+ "_name": name,
577
+ "_objFlags": 0,
578
+ "_parent": { "__id__": parentId },
579
+ "_children": [],
580
+ "_active": options.active !== false,
581
+ "_components": [],
582
+ "_prefab": { "__id__": prefabInfoIndex },
583
+ "_opacity": 255,
584
+ "_color": { "__type__": "cc.Color", "r": 255, "g": 255, "b": 255, "a": 255 },
585
+ "_contentSize": { "__type__": "cc.Size", "width": options.width || 0, "height": options.height || 0 },
586
+ "_anchorPoint": { "__type__": "cc.Vec2", "x": 0.5, "y": 0.5 },
587
+ "_trs": { "__type__": "TypedArray", "ctor": "Float64Array", "array": [options.x || 0, options.y || 0, 0, 0, 0, 0, 1, 1, 1, 1] },
588
+ "_eulerAngles": { "__type__": "cc.Vec3", "x": 0, "y": 0, "z": 0 },
589
+ "_skewX": 0,
590
+ "_skewY": 0,
591
+ "_is3DNode": false,
592
+ "_groupIndex": 0,
593
+ "groupIndex": 0,
594
+ "_id": ""
595
+ };
596
+
597
+ const prefabInfo = {
598
+ "__type__": "cc.PrefabInfo",
599
+ "root": { "__id__": rootId },
600
+ "asset": { "__id__": 0 },
601
+ "fileId": fileId,
602
+ "sync": false
603
+ };
604
+
605
+ return { nodeData, prefabInfo };
606
+ }
607
+
608
+ /**
609
+ * 获取预制体根节点索引
610
+ */
611
+ function getPrefabRootIndex(data) {
612
+ if (!isPrefab(data)) return null;
613
+ return 1; // 预制体根节点固定在索引 1
614
+ }
615
+
401
616
  module.exports = {
402
617
  loadScene,
403
618
  saveScene,
@@ -408,5 +623,12 @@ module.exports = {
408
623
  reorderArrayToMatchChildren,
409
624
  refreshEditor,
410
625
  installPlugin,
411
- checkPluginStatus
626
+ checkPluginStatus,
627
+ loadScriptMap,
628
+ buildTree,
629
+ isPrefab,
630
+ createPrefab,
631
+ createPrefabNodeData,
632
+ getPrefabRootIndex,
633
+ generateFileId
412
634
  };