main.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. 'use strict';
  2. // Anim Studio 导入器 —— Cocos Creator 3.8.x 编辑器扩展
  3. // 菜单:扩展 → Anim Studio → 1.导入 cocos-pack 资源 / 2.布置演示节点
  4. const fs = require('fs');
  5. const path = require('path');
  6. function projectAssets() {
  7. return path.join(Editor.Project.path, 'assets');
  8. }
  9. // 递归拷贝(Node 16+ 自带 fs.cpSync;做个兜底)
  10. function copyDir(src, dst) {
  11. if (fs.cpSync) { fs.cpSync(src, dst, { recursive: true }); return; }
  12. fs.mkdirSync(dst, { recursive: true });
  13. for (const name of fs.readdirSync(src)) {
  14. const s = path.join(src, name), d = path.join(dst, name);
  15. if (fs.statSync(s).isDirectory()) copyDir(s, d);
  16. else fs.copyFileSync(s, d);
  17. }
  18. }
  19. exports.methods = {
  20. // ---------- 1. 导入资源 ----------
  21. async importPack() {
  22. let picked;
  23. try {
  24. picked = await Editor.Dialog.select({
  25. title: '选择 Anim Studio 导出的 cocos-pack 文件夹',
  26. path: Editor.Project.path,
  27. type: 'directory',
  28. });
  29. } catch (e) {
  30. return Editor.Dialog.error('Anim Studio', { detail: '打开文件选择框失败:' + e });
  31. }
  32. if (!picked || picked.canceled || !picked.filePaths || !picked.filePaths.length) return;
  33. const pack = picked.filePaths[0];
  34. // 兼容:用户既可能选 cocos-pack 本身,也可能选到里面的 assets
  35. let srcAssets = path.join(pack, 'assets');
  36. if (!fs.existsSync(srcAssets) && fs.existsSync(path.join(pack, 'resources'))) {
  37. srcAssets = pack; // 选到了 assets 目录
  38. }
  39. if (!fs.existsSync(srcAssets)) {
  40. return Editor.Dialog.error('Anim Studio', {
  41. detail: '没找到 assets/(或 resources/)。\n你要选的是 Anim Studio 导出的「cocos-pack」文件夹(里面有 assets/resources 和 assets/scripts),不是你的 Cocos 项目文件夹。',
  42. });
  43. }
  44. const dstAssets = projectAssets();
  45. // 防呆:选到了项目自己(src 与 dest 同根),会导致“自己拷给自己”
  46. if (path.resolve(srcAssets) === path.resolve(dstAssets) ||
  47. path.resolve(srcAssets).startsWith(path.resolve(dstAssets) + path.sep)) {
  48. return Editor.Dialog.error('Anim Studio', {
  49. detail: '你选到了当前项目自己的 assets 了。\n请改选 Anim Studio 网站「导出」生成的 cocos-pack 文件夹(通常在 out/<game>/cocos-pack/),它里面才有要导入的素材。',
  50. });
  51. }
  52. const copied = [];
  53. try {
  54. for (const sub of ['resources', 'scripts']) {
  55. const s = path.join(srcAssets, sub);
  56. if (!fs.existsSync(s)) continue;
  57. const d = path.join(dstAssets, sub);
  58. // 再保险:逐个子目录也跳过 src===dest
  59. if (path.resolve(s) === path.resolve(d)) continue;
  60. copyDir(s, d);
  61. copied.push(sub);
  62. }
  63. } catch (e) {
  64. return Editor.Dialog.error('Anim Studio', { detail: '拷贝文件失败:' + e });
  65. }
  66. if (!copied.length) {
  67. return Editor.Dialog.error('Anim Studio', { detail: 'assets 里没有 resources / scripts 子目录。' });
  68. }
  69. // 让编辑器重新导入资源数据库
  70. try { await Editor.Message.request('asset-db', 'refresh-asset', 'db://assets'); } catch (e) {}
  71. Editor.Dialog.info('Anim Studio', {
  72. detail: '已导入:' + copied.join('、') +
  73. '。\n等右下角导入进度条走完后,新建或打开一个场景,再点菜单「2. 在当前场景布置演示节点」。',
  74. buttons: ['好的'],
  75. });
  76. },
  77. // ---------- 2. 布置演示节点 ----------
  78. async buildScene() {
  79. // 先确认有打开的场景
  80. let scene;
  81. try { scene = await Editor.Message.request('scene', 'query-is-ready'); } catch (e) {}
  82. try {
  83. // 找一个 Canvas 作父节点(新建 2D 场景默认就有,名为 Canvas)
  84. let parent = null;
  85. try {
  86. const tree = await Editor.Message.request('scene', 'query-node-tree');
  87. parent = findCanvasUuid(tree);
  88. } catch (e) {}
  89. const opt = { name: 'AnimStudioDemo' };
  90. if (parent) opt.parent = parent;
  91. const nodeUuid = await Editor.Message.request('scene', 'create-node', opt);
  92. const uuid = typeof nodeUuid === 'string' ? nodeUuid : (nodeUuid && nodeUuid.uuid);
  93. await Editor.Message.request('scene', 'create-component', { uuid, component: 'JellyDemo' });
  94. Editor.Dialog.info('Anim Studio', {
  95. detail: '已创建节点「AnimStudioDemo」并挂上 JellyDemo 组件。\n' +
  96. '按 Ctrl/Cmd+S 保存场景,再点上方 ▶ 播放即可。\n' +
  97. (parent ? '' : '(没检测到 Canvas,如果画面空白,请确认场景里有 Canvas,并把该节点拖到 Canvas 下。)'),
  98. buttons: ['完成'],
  99. });
  100. } catch (e) {
  101. Editor.Dialog.error('Anim Studio', {
  102. detail: '自动布置失败(多半是没有打开场景,或脚本还没导入完):' + e +
  103. '\n\n手动兜底:新建/打开一个场景 → 在 Canvas 下右键新建空节点 → 把 assets/scripts/JellyDemo.ts 拖到它的属性检查器上 → 保存并播放。',
  104. });
  105. }
  106. },
  107. };
  108. // 在节点树里找第一个名为 Canvas 的节点 uuid
  109. function findCanvasUuid(node) {
  110. if (!node) return null;
  111. if (Array.isArray(node)) {
  112. for (const n of node) { const r = findCanvasUuid(n); if (r) return r; }
  113. return null;
  114. }
  115. if (node.name === 'Canvas' && node.uuid) return node.uuid;
  116. if (node.children) return findCanvasUuid(node.children);
  117. return null;
  118. }
  119. exports.load = function () {};
  120. exports.unload = function () {};