cocos2d-cli 1.0.5 → 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,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
  /**
@@ -413,52 +429,190 @@ function loadScriptMap(scenePath) {
413
429
  }
414
430
 
415
431
  /**
416
- * 构建节点树输出
432
+ * 构建节点树输出(自动适配场景和预制体)
417
433
  */
418
434
  function buildTree(data, scriptMap, nodeIndex, prefix = '', isLast = true, isRoot = true) {
419
435
  const node = data[nodeIndex];
420
436
  if (!node) return '';
421
437
 
438
+ // 跳过 Scene 类型(场景根节点)
439
+ const isSceneRoot = node.__type__ === 'cc.Scene';
422
440
  const nodeName = isRoot ? 'Root' : (node._name || '(unnamed)');
423
441
  const active = node._active !== false ? '●' : '○';
424
442
  const uuidRegex = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i;
425
443
 
426
- let result = prefix + (isRoot ? '' : active + ' ') + nodeName + ' #' + nodeIndex;
427
-
428
- // 添加组件信息
429
- if (node._components && node._components.length > 0) {
430
- const comps = node._components.map(c => {
431
- const comp = data[c.__id__];
432
- if (!comp) return `? #${c.__id__}`;
433
- const typeName = comp.__type__;
434
- let displayName;
435
- if (uuidRegex.test(typeName)) {
436
- const scriptInfo = scriptMap[typeName];
437
- displayName = (scriptInfo && scriptInfo.name) ? scriptInfo.name : '⚠️MissingScript';
438
- } else if (typeName === 'MissingScript') {
439
- displayName = '⚠️MissingScript';
440
- } else {
441
- displayName = typeName.replace('cc.', '');
442
- }
443
- return `${displayName} #${c.__id__}`;
444
- }).join(', ');
445
- result += ` (${comps})`;
446
- }
444
+ let result = '';
447
445
 
448
- result += '\n';
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
+ }
449
474
 
450
475
  // 处理子节点
451
476
  if (node._children && node._children.length > 0) {
452
477
  node._children.forEach((childRef, idx) => {
453
478
  const childIsLast = idx === node._children.length - 1;
454
- const childPrefix = prefix + (isRoot ? '' : (isLast ? ' ' : '│ '));
455
- result += buildTree(data, scriptMap, childRef.__id__, childPrefix, childIsLast, false);
479
+ const childPrefix = prefix + (isSceneRoot ? '' : (isRoot ? '' : (isLast ? ' ' : '│ ')));
480
+ result += buildTree(data, scriptMap, childRef.__id__, childPrefix, childIsLast, isSceneRoot);
456
481
  });
457
482
  }
458
483
 
459
484
  return result;
460
485
  }
461
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
+
462
616
  module.exports = {
463
617
  loadScene,
464
618
  saveScene,
@@ -471,5 +625,10 @@ module.exports = {
471
625
  installPlugin,
472
626
  checkPluginStatus,
473
627
  loadScriptMap,
474
- buildTree
628
+ buildTree,
629
+ isPrefab,
630
+ createPrefab,
631
+ createPrefabNodeData,
632
+ getPrefabRootIndex,
633
+ generateFileId
475
634
  };