# 网页粒子系统 · 阶段十一 > 阶段定位:**Spine JSON 导出体系、拖尾蒙皮定版与曲线编辑能力完善** > 完成日期:2026-08-31 > 工程目录:`/Users/tianmokeji/Desktop/SpineParticle` > 技术栈:Vue 3 + TypeScript + Pinia + PixiJS 7 + Spine Runtime 4.2 + Vite --- ## 一、阶段结论 阶段十一在阶段十的序列帧和循环持续动画体系上,完成了第一版可重新导入 Spine 编辑器继续编辑的 Spine 4.2 JSON 导出功能,并围绕实际导入结果持续修正了骨骼层级、关键帧数量、加权 Mesh 拓扑、拖尾生命周期和首帧语义。 本阶段没有把粒子效果导出为不可编辑的逐帧图片,而是把粒子运行结果烘焙为 Spine 可编辑结构:粒子使用独立骨骼和插槽,拖尾使用同级骨骼组与加权 Mesh,运动、旋转、缩放和 RGBA 进入 Spine 时间轴。碰撞、路径和 Spine 预览对象不直接导出,与粒子有关的最终表现均烘焙到粒子动画数据中。 本阶段主要完成: 1. 实现 Spine 4.2 JSON 导出设置与文件下载; 2. 建立独立导出模块、关键帧精简模块、Mesh 构建模块和结构校验模块; 3. 粒子系统导出为系统骨骼、粒子骨骼、插槽和附件; 4. 拖尾导出为固定拓扑加权 Mesh 与同级拖尾骨骼组; 5. 图片只写资源路径,不把图片文件写入 JSON; 6. 隐藏粒子系统不导出,图片缺失时仍保留附件路径; 7. 支持固定图和序列帧附件时间轴; 8. 支持线性与平滑贝塞尔关键帧精简; 9. 按数据类型设置导出误差容限; 10. 修正 Spine 版本字段为 `4.2`; 11. 修正拖尾 Mesh 的 hull、edges、三角形和权重结构; 12. 粒子骨骼增加粒子系统父级,拖尾骨骼改为同级结构; 13. 拖尾整组骨骼同生同死,假死改为插槽透明度控制; 14. 拖尾未展开阶段同时插值位置和旋转; 15. 曲线编辑器实现真正的贝塞尔控制手柄; 16. “跟随导出”模式正确读取全局插值设置,不再反向覆盖设置; 17. 普通发射模式第 0 帧固定为空,循环持续保留独立衔接规则; 18. 修复 `update(0)` 导致爆发数量重复的问题; 19. 画布骨骼调试增加固定屏幕尺寸的方向轴; 20. 拖尾资源增加拖尾比例以及固定/随机长度、宽度; 21. 增加新手使用说明弹窗; 22. 完成 TypeScript、Vite、浏览器交互和 Spine Runtime 解析验证。 详细且长期有效的导出约束另见: ```text 导出设计标准规范.md ``` 阶段十一文档记录实际开发结果;导出设计标准规范记录后续导出功能必须继续遵守的标准。 --- ## 二、Spine JSON 导出范围 ### 2.1 导出对象 当前只导出可见粒子系统: ```text visible !== false ``` 不会直接导出: - 碰撞对象; - 场景路径; - 加载进编辑器用于预览的 Spine 对象; - 背景图; - 画布网格、发射点和调试骨骼; - 编辑器 UI 状态。 如果粒子运动受到碰撞、路径或其他模块影响,导出的是已经计算完成的粒子最终位置、旋转、缩放和颜色,不保留对外部模块的运行时引用。 ### 2.2 图片资源 JSON 不内嵌 PNG,也不额外复制图片。附件只写入正确的图片资源路径。导入 Spine 后,由用户重新选择图片文件夹完成扫描。 图片缺失不会阻止导出。导出器会: 1. 保留原图片文件名和路径; 2. 使用可用尺寸或安全占位尺寸建立附件; 3. 在导出结果中返回资源提示; 4. 允许用户在 Spine 中按文件名补齐图片。 默认图片路径: ```text ./images/ ``` ### 2.3 文件名和版本 默认导出文件名: ```text SpineParticle.json ``` 骨架版本字段固定为: ```json { "skeleton": { "spine": "4.2" } } ``` 第一版设置面板只提供 Spine 4.2,不伪装支持其他版本。 --- ## 三、导出设置 ### 3.1 当前参数 导出设置模块包含: | 参数 | 当前规则 | |---|---| | 图片路径 | 写入 `skeleton.images` | | Spine 版本 | 固定 Spine 4.2 | | 关键帧插值曲线 | 线性或平滑贝塞尔 | | 导出 FPS | 默认 30 | | 不写 fps | 只省略元数据,不改变动画时间 | | 整数帧对齐 | 调整采样帧率时保持实际秒数不变 | | 骨骼对象池 | 默认开启,稳定复用粒子骨骼轨道 | | 排除无内容动画 | 按开关决定是否保留空动画 | ### 3.2 FPS 规则 修改导出 FPS 时保持动画实际秒数不变。 例如原动画为 30 FPS、30 帧,即 1 秒;改为 60 FPS 后会重采样到约 60 帧,仍保持 1 秒,而不是把 30 帧直接解释为 0.5 秒。 “不写 fps”只影响: ```text skeleton.fps ``` 不会改变关键帧的 `time` 秒数。 ### 3.3 空动画 空动画是否保留完全遵循“排除无内容动画”设置。 关闭开关时,即使动画当前没有粒子时间轴,也保留空动画名称,方便后续在 Spine 工程内继续编辑或与其他动画合并。 --- ## 四、导出骨骼层级 ### 4.1 粒子系统父级 每个粒子系统建立一根系统骨骼,直接挂在 `root` 下: ```text root └─ ParticleSystem1 ``` 粒子骨骼和拖尾骨骼全部直接挂在粒子系统骨骼下: ```text root └─ ParticleSystem1 ├─ _particle_000 ├─ _particle_000_trail_1_0 ├─ _particle_000_trail_1_1 └─ _particle_000_trail_1_2 ``` 不再使用以下错误结构: ```text root ├─ ParticleSystem1_particle_000 ├─ ParticleSystem1_particle_001 └─ ParticleSystem1_particle_002 ``` ### 4.2 拖尾骨骼不组成骨骼链 早期导出把拖尾骨骼首尾相接: ```text trail_0 └─ trail_1 └─ trail_2 ``` 这种结构会让子骨骼继承父骨骼的位置、旋转和缩放,导致世界变换逐级累积。网页画布与 Spine 编辑器中的拖尾表现因此不一致。 当前规则是所有拖尾骨骼同级: ```text ParticleSystem1 ├─ trail_0 ├─ trail_1 └─ trail_2 ``` 每根拖尾骨骼独立写入: - 世界位置; - 世界旋转; - 宽度和形状需要的缩放。 导出校验器会阻止拖尾骨骼再次组成父子链。 ### 4.3 设置姿态 所有系统骨骼和粒子骨骼的设置姿态保持安全默认值: ```text 位置:0, 0 旋转:0 缩放:1, 1 ``` 拖尾同级骨骼也从原点设置姿态开始,实际位置和旋转写入动画时间轴,避免设置姿态破坏 Mesh 蒙皮。 附件默认颜色使用透明状态,导入 Spine 后不会在设置模式中干扰已有图像显示。 --- ## 五、粒子附件和动画 ### 5.1 粒子图片 每根粒子骨骼根据图片混合模式创建相应插槽。支持: - normal; - additive; - multiply; - screen。 固定图片使用区域附件。序列帧图片在同一插槽中通过 attachment 时间轴切换附件,不会为每张序列图创建新骨骼。 ### 5.2 粒子动画时间轴 粒子骨骼导出: - translate; - rotate; - scale。 粒子插槽导出: - attachment; - rgba。 粒子出生、死亡、资源切换和序列帧切换会被识别为强制边界,关键帧精简不能跨过这些离散事件破坏状态。 ### 5.3 骨骼对象池 开启骨骼对象池后,同一粒子池槽位在多个出生周期中复用同一根导出骨骼。粒子死亡阶段通过附件显隐和 RGBA 时间轴表达,而不是不断创建新的骨骼名称。 该规则显著降低高发射率粒子系统的骨骼数量,并让重新导入 Spine 后的层级更适合继续编辑。 --- ## 六、关键帧精简 ### 6.1 数据容差 阶段十一最终采用以下导出容差: | 数据 | 容差 | |---|---:| | 粒子位置 | 0.1 像素 | | 拖尾骨骼位置 | 0.1 像素 | | 旋转 | 0.01° | | 缩放 | 0.01 | | 透明度 | 0.01 | | RGB | 单通道 1/255 | 这些数值用于判断中间关键帧是否可以由相邻关键帧安全重建。 ### 6.2 线性模式 线性模式使用误差受控的数值关键帧精简。如果一段位置、旋转、缩放或颜色变化可以由线性插值在容差内还原,则只保留必要端点。 出生、死亡、附件切换和突变帧不会被普通线性精简跨越。 ### 6.3 平滑贝塞尔 平滑贝塞尔模式对逐帧烘焙结果进行分段三次曲线拟合,并把控制值写入 Spine 曲线数据。 该模式主要用于: - 曲线运动; - 非线性透明度; - 平滑颜色变化; - 受修改器影响的连续轨迹。 拟合不会过度激进,优先保留动画精度和 Spine 编辑器中的可编辑性。 ### 6.4 旋转展开 旋转精简前会先展开跨越 `-180°/180°` 的角度序列,避免真实连续旋转被误判为反方向大跳变。 --- ## 七、曲线编辑器 ### 7.1 三种模式 带“曲线模式”的参数支持: | 模式 | 行为 | |---|---| | 跟随导出 | 读取导出设置的关键帧插值模式 | | 线性 | 当前参数强制使用线性 | | 贝塞尔 | 当前参数强制使用贝塞尔 | “跟随导出”是单向读取关系: ```text 导出设置 → 当前曲线参数 ``` 它不会反向修改导出设置。 ### 7.2 贝塞尔控制手柄 曲线编辑器的贝塞尔模式现在会显示: - 节点输入手柄; - 节点输出手柄; - 节点到手柄的辅助线; - 三次贝塞尔实际曲线路径。 手柄位置作为曲线点的相对数据保存: ```text inX / inY outX / outY ``` 这些字段自动进入配置保存、加载、预设和撤回快照。 未手动调整的贝塞尔手柄默认位于相邻节点三分之一位置,因此初始外观仍与线性一致,但用户可以拖动手柄改变曲率。 ### 7.3 当前联动范围 当前已接入曲线模式解析的主要参数: - 粒子系统透明度曲线; - 图片资源独立透明度曲线; - 碰撞对象路径跟随曲线。 粒子实时预览与导出使用同一套“跟随导出”解析结果。 --- ## 八、拖尾加权 Mesh ### 8.1 固定拓扑 每个拖尾附件只建立一次固定拓扑 Mesh。生命周期中不会改变: - 顶点数量; - UV 数量; - 三角形索引; - hull 顶点数量; - edges; - 顶点骨骼权重结构。 动画只改变拖尾骨骼和插槽数据,绝不在不同帧生成不同数量的网格顶点。 ### 8.2 hull 与顶点顺序 Spine JSON 的 `hull` 表示顶点数组开头连续存放的外轮廓顶点数量。Mesh 构建器会先按顺时针顺序写入四周轮廓,再追加内部网格点。 该规则修复了早期导入 Spine 后出现的异常外轮廓、顶点连线和导入失败问题。 ### 8.3 权重 每个网格列最多受相邻两根拖尾骨骼影响。权重之和严格为 1。 拖尾骨骼改成同级后,顶点局部位置不再依赖链式设置姿态偏移。每列中心主要由骨骼动画位置与相邻骨骼权重插值得到,网格宽度由骨骼 Y 缩放和局部横向顶点共同表达。 ### 8.4 导入前校验 下载前会检查: - UV 数量是否合法; - 顶点编码是否合法; - 三角形是否越界; - hull 是否越界; - edges 是否为有效偶数索引; - 骨骼索引是否存在; - 每个顶点权重之和是否为 1; - 加权 Mesh 是否使用了空附件; - 拖尾骨骼是否错误组成骨骼链。 随后使用项目内 Spine 4.2 Runtime 的 `SkeletonJson` 再解析一次,只有通过后才触发下载。 --- ## 九、拖尾骨骼生命周期 ### 9.1 整组出生和死亡 一个拖尾 Mesh 绑定的全部骨骼属于同一个固定骨骼组。只要该粒子生成拖尾,这组骨骼必须: - 同一帧出生; - 同一帧进入活动状态; - 同步移动; - 同一帧死亡或假死。 禁止根据轨迹长度逐节激活骨骼。旧字段 `activeBoneCount` 只保留配置兼容意义,当前有效状态始终等于拖尾骨骼总数。 ### 9.2 未完全展开时的位置 拖尾刚生成时,历史轨迹可能短于配置长度。当前规则不是把后方骨骼全部堆叠在最旧点,而是: 1. 从粒子出生点开始记录首个轨迹段; 2. 计算当前已经积累的有效轨迹长度; 3. 在有效轨迹内把全部拖尾骨骼等距分布; 4. 随有效轨迹增长,整组骨骼逐渐展开到完整配置长度。 因此即使拖尾尚未达到完整长度,Mesh 也始终由完整骨骼组驱动。 ### 9.3 未完全展开时的旋转 位置插值期间,旋转也必须平滑。 当前做法: 1. 计算头部切线方向; 2. 计算当前尾端切线方向; 3. 将角度差转换为最短旋转方向; 4. 按骨骼序号在头尾角度之间插值; 5. 达到完整轨迹长度后恢复按真实局部切线计算。 这样可以避免同级骨骼方向突然折跳造成网格不平滑。 缩放不参与这段“轨迹不足”的补间,继续由拖尾宽度和形状参数控制。 ### 9.4 假死方式 早期方案把尚未出现或死亡的拖尾骨骼缩放到 0。整组骨骼生命周期统一后,不再需要逐骨骼缩放假死。 当前规则: ```text 拖尾存在:插槽 alpha = 实际透明度 拖尾不存在:插槽 alpha = 0 ``` 骨骼缩放始终保留,原因是缩放还负责: - 拖尾宽度; - 粒子宽度跟随; - 拖尾形状曲线; - 后续在 Spine 中继续编辑。 出生前使用首次有效骨骼姿态但插槽透明,死亡后保持最后有效骨骼姿态但插槽透明。Mesh 和骨骼对象不会在时间轴中删除和重建。 --- ## 十、拖尾比例与随机尺寸 ### 10.1 拖尾比例 每张拖尾资源卡片新增: ```text 拖尾比例 (%) ``` 有效范围为 0~100: | 数值 | 行为 | |---:|---| | 0 | 选中该资源的粒子均不生成拖尾 | | 50 | 约一半粒子生成拖尾 | | 100 | 所有选中该资源的粒子生成拖尾 | 判断发生在粒子出生时,使用粒子系统随机种子和周期内出生序号,因此相同配置与种子可以稳定复现。 当当前发射段计算出的粒子数量为 1、拖尾比例大于 0 时,第一颗粒子强制生成拖尾,避免开启拖尾却因概率采样完全看不到效果。比例明确设置为 0 时不执行保底。 ### 10.2 长度和宽度模式 拖尾长度与宽度分别支持: | 模式 | 参数 | |---|---| | 固定 | 单一比例值 | | 随机范围 | 最小比例和最大比例 | 默认值: ```text 固定长度:100% 固定宽度:100% 随机最小:100% 随机最大:100% ``` 尺寸解释: | 数值 | 行为 | |---:|---| | 0 | 不生成拖尾 | | 100 | 使用标准长度或宽度 | | 200 | 标准尺寸的 2 倍 | 为支持超过 100 的比例放大,界面当前允许输入 0~500。 随机长度和宽度分别使用独立确定性采样,不会因为其他随机参数增加或删除而改变结果。 ### 10.3 导出适配 不同粒子可以拥有不同拖尾长度和宽度: - 长度通过每根拖尾骨骼的实际动画位置表达; - 宽度通过骨骼 Y 缩放表达; - 形状曲线继续叠加到各骨骼宽度缩放; - 没有生成拖尾的粒子不创建对应拖尾动画内容; - 某个资源在整条轨道中从未出现拖尾时,不创建无用 Mesh 和拖尾骨骼。 --- ## 十一、第 0 帧与发射规则 ### 11.1 普通发射模式 以下模式第 0 帧固定为空: - 爆发; - 常规持续。 所有粒子发射行为从第 1 帧开始。第 0 帧画布中: ```text 粒子数量 = 0 粒子骨骼数量 = 0 拖尾骨骼数量 = 0 ``` ### 11.2 循环持续例外 循环持续不受普通第 0 帧空白规则约束,而是继续使用阶段十已经确定的三段逻辑: | 循环状态 | 第 0 帧 | |---|---| | 生成开始循环动画 | 空帧 | | 纯循环 | 循环衔接帧,可包含骨骼 | | 生成结束循环动画 | 循环衔接帧,可包含骨骼 | 缓存生成层和画布显示层都按粒子系统分别判断,不会因为同一场景中存在普通系统而错误清空循环系统的衔接帧。 ### 11.3 爆发重复修复 时间轴固定帧之间会调用: ```text update(0) ``` 用于保持当前画面而不推进时间。旧爆发判断使用: ```text burstTimer <= dt ``` 当 `dt = 0` 时,`0 <= 0` 被误认为第一次爆发,导致设置单次数量为 1 时实际出现 2 个粒子。 当前增加明确限制: ```text dt > 0 ``` 零时间刷新不再触发发射。浏览器回归验证结果: ```text 单次数量 = 1 第 0 帧粒子 = 0 第 1 帧粒子 = 1 ``` --- ## 十二、画布骨骼调试 ### 12.1 骨骼方向轴 开启系统设置中的“骨骼连线”后,每个粒子骨骼和拖尾骨骼点都会显示局部方向轴: | 颜色 | 含义 | |---|---| | 青色 | 骨骼正方向 | | 黄色 | 与正方向垂直的辅助轴 | 粒子方向轴会处理网页画布 Y 轴向下与数学旋转方向的差异;拖尾方向轴直接使用拖尾轨迹切线旋转。 画布中的拖尾连接线也已改为同级语义,不再把相邻拖尾骨骼画成父子链。 ### 12.2 固定屏幕尺寸 骨骼点和方向轴的视觉尺寸不随画布缩放改变。绘制前先把目标屏幕像素除以当前画布缩放,确保放大或缩小画布后调试标记仍保持相同大小。 系统设置新增: ```text 骨骼点绘制大小:默认 1 坐标轴绘制大小:默认 2 ``` 有效范围为 0.1~10。参数会进入: - 配置保存; - 配置加载; - 预设; - 撤回快照。 旧配置缺少字段时自动恢复默认值。 --- ## 十三、使用说明 画布右上角增加“使用说明”按钮。点击后显示可关闭的新手说明弹窗,内容包括: - 创建和选择场景对象; - 粒子常用面板; - 时间轴播放和逐帧查看; - 图片、序列帧和拖尾资源; - 配置保存、加载和预设; - Spine JSON 导出; - 常用快捷键。 弹窗不会改变编辑器配置,也不会阻塞画布以外的配置数据。 --- ## 十四、配置、撤回与兼容 阶段十一新增的重要配置字段包括: ```text 曲线点:inX / inY / outX / outY 系统设置:bonePointDrawSize / boneAxisDrawSize 拖尾资源:trailRatio 拖尾资源:lengthMode / lengthMin / lengthMax 拖尾资源:widthSizeMode / widthMin / widthMax ``` 这些字段会自动进入编辑器 JSON 配置、预设、对象复制和撤回快照。 拖尾资源版本升级到: ```text trailResourceVersion = 6 ``` 旧资源迁移规则: - 缺少拖尾比例时使用 100; - 缺少长度模式时使用固定; - 长度随机范围初始化为旧固定长度; - 缺少宽度模式时使用固定; - 宽度随机范围初始化为旧固定宽度; - 旧配置已有的长度和宽度数值保持不变,不强制覆盖为 100; - 新建资源和重置资源使用 100 的新默认值。 --- ## 十五、主要代码落点 | 文件 | 阶段十一职责 | |---|---| | `src/export/spineJsonExporter.ts` | Spine JSON 文档、骨骼层级、动画时间轴、拖尾整组显隐、文件下载 | | `src/export/spineKeyframeReducer.ts` | 线性与贝塞尔关键帧误差精简、控制值拟合、角度展开 | | `src/export/spineMeshBuilder.ts` | 同级拖尾骨骼加权 Mesh、UV、三角形、hull、edges 和顶点权重 | | `src/export/spineJsonValidator.ts` | Mesh 结构、拖尾层级与 Spine 4.2 Runtime 解析校验 | | `src/export/spineExportSettings.ts` | 导出参数默认值与旧设置补齐 | | `src/core/particleEmitter.ts` | 贝塞尔曲线采样、爆发修复、拖尾比例、随机尺寸、整组骨骼插值和录制状态 | | `src/views/CurveEditor.vue` | SVG 贝塞尔路径、输入/输出控制手柄和节点编辑 | | `src/views/ParticlePanel.vue` | 导出设置、拖尾资源比例、固定/随机尺寸、曲线模式与导出按钮 | | `src/views/Stage.vue` | 第 0 帧规则、循环例外、骨骼方向轴、固定调试尺寸、缓存录制和回放 | | `src/store/particleStore.ts` | 新系统设置字段和时间轴状态 | | `src/editor/editorConfig.ts` | 新字段保存、加载、旧配置补齐与纹理恢复 | | `导出设计标准规范.md` | Spine 导出长期标准与不可回退约束 | --- ## 十六、验证结果 ### 16.1 构建 阶段结束执行: ```text npm run build ``` 结果: - `vue-tsc -b` 通过; - Vite 生产构建通过; - 553 个模块完成转换; - 无 TypeScript 错误; - 无构建错误; - 仅保留既有的大包体积提示。 ### 16.2 Spine JSON 使用 TrailTest 数据构造拖尾导出样例并通过项目内 Spine 4.2 Runtime 解析: ```text 拖尾骨骼数量:3 拖尾骨骼父级:全部为 ParticleSystem1 动画数量:1 Mesh 顶点和 UV:固定且匹配 ``` 整组假死验证: ```text 骨骼缩放归零:false 插槽透明度:00 → bf → 00 ``` 即骨骼缩放完整保留,拖尾通过统一插槽透明度出现和消失。 ### 16.3 浏览器交互 已验证: - 贝塞尔模式显示控制手柄; - 跟随导出 + 线性时不显示贝塞尔手柄; - 跟随导出 + 平滑贝塞尔时显示手柄; - 第 0 帧普通粒子数量为 0; - 爆发数量 1 时第 1 帧粒子数量为 1; - 骨骼方向轴正常显示; - 骨骼点绘制大小默认 1; - 坐标轴绘制大小默认 2; - 拖尾比例默认 100; - 长度和宽度可切换固定与随机范围; - 随机范围初值正确继承当前固定值。 --- ## 十七、阶段十一最终状态 阶段十一完成后,编辑器已经具备: - 可重新导入 Spine 4.2 编辑器的粒子 JSON; - 可编辑粒子骨骼、插槽和附件; - 固定拓扑加权拖尾 Mesh; - 同级拖尾骨骼结构; - 整组拖尾骨骼生命周期; - 插槽透明度假死; - 未展开拖尾的位置与旋转平滑插值; - 误差受控的关键帧精简; - 线性和贝塞尔导出模式; - 可操作的贝塞尔曲线手柄; - 普通发射第 0 帧空白规则; - 循环持续独立第 0 帧衔接规则; - 正确的单次爆发数量; - 固定屏幕尺寸的骨骼点与方向轴; - 拖尾比例与确定性随机长度、宽度; - 配置、预设、复制和撤回完整联动; - 下载前 Spine 4.2 结构校验。 至此,阶段十一“Spine JSON 导出体系与拖尾蒙皮定版”完成落盘。