Files
SpineParticlesWeb/Document/网页粒子系统阶段十.md
T
2026-09-02 10:45:10 +08:00

521 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 网页粒子系统 · 阶段十
> 阶段定位:**粒子图片序列帧与循环持续三段动画体系定版**
> 完成日期:2026-08-31
> 工程目录:`/Users/tianmokeji/Desktop/SpineParticle`
> 技术栈:Vue 3 + TypeScript + Pinia + PixiJS 7 + Spine Runtime 4.2 + Vite
---
## 一、阶段结论
阶段十在阶段九配置持久化、撤回和预设体系的基础上,扩展了粒子图片资源的序列帧能力,并完成了持续发射模式下“启动—循环—结束”三段动画的编辑和预览模型。
图片资源现在可以在固定图片和序列帧之间切换。每个序列帧资源仍属于同一个粒子和同一根粒子骨骼,只在播放过程中替换纹理,不会因为一组序列图而生成多根粒子骨骼。
持续发射模式新增“常规持续”和“循环持续”。循环持续通过稳定周期预演、确定性随机序列、完整帧缓存和假死粒子状态解决首尾衔接问题,并支持分别生成启动段、纯循环段和结束段。
本阶段主要完成:
1. 图片资源增加固定图片与序列帧两种模式;
2. 支持多选图片并按自然文件名顺序组成序列;
3. 支持单次尾帧消失、单次尾帧固定、正向循环和反向循环;
4. 支持独立序列帧播放帧率;
5. 序列帧状态纳入粒子录制、回放、保存、加载和撤回;
6. 持续发射增加常规持续与循环持续两种表现;
7. 循环时长使用独立整数帧参数,不覆盖常规持续秒数;
8. 实现启动段、纯循环段、结束段三种互斥表现;
9. 建立统一的循环衔接帧;
10. 使用周期预演避免末帧硬复制造成的运动突变;
11. 使用透明度为 0 的假死粒子保持纯循环段骨骼集合稳定;
12. 支持循环模式下的延迟时间与首帧保持;
13. 循环模式自动禁用并重置 Spine 骨骼跟随和路径跟随;
14. 修复循环重新计算期间缓存帧与实时状态交替造成的闪烁;
15. 时间轴支持选择包含结束端点的最后一帧;
16. 多粒子系统独立计算,整体时间轴继续取粒子和 Spine 中最长者;
17. 完成 TypeScript 与 Vite 生产构建验证。
---
## 二、图片序列帧资源
### 2.1 图片模式
每张粒子图片资源卡片增加“图片模式”:
| 模式 | 说明 |
|---|---|
| 固定 | 粒子生命周期内始终使用同一张图片 |
| 序列帧 | 按粒子出生后的时间依次切换多张图片 |
模式属于单个图片资源卡片。一个粒子系统可以同时存在多个图片资源,每个资源可以分别选择固定图片或序列帧,并继续使用原有权重随机选择逻辑。
资源卡片原有参数保持不变,包括:
- 权重与权重锁定;
- 缩放;
- 锚点;
- 颜色模式与颜色渐变;
- 混合模式;
- 独立透明度与透明度曲线。
### 2.2 序列图片选择
选择序列帧时允许一次多选图片。编辑器会按文件名进行自然排序,而不是简单字符串排序。
例如:
```text
diwen_1.png
diwen_2.png
diwen_10.png
```
会按照 `1 → 2 → 10` 排列。
界面明确显示实际播放顺序:
```text
已选择 13 帧,按 diwen_000.png-diwen_012.png 顺序播放
```
阶段测试资源位于:
```text
public/序列帧测试/sg/
```
### 2.3 播放模式
序列帧支持四种播放模式:
| 模式 | 行为 |
|---|---|
| 单次-尾帧消失 | 正向播放一次,超过最后一张后隐藏粒子图片 |
| 单次-尾帧固定 | 正向播放一次,之后保持最后一张图片 |
| 正向循环 | 第一张播放到最后一张,再回到第一张 |
| 反向循环 | 最后一张播放到第一张,再回到最后一张 |
“尾帧消失”只隐藏粒子图片。粒子逻辑、粒子骨骼和拖尾仍可继续存在,直到粒子生命周期结束。
### 2.4 播放帧率
每个序列帧资源拥有独立播放帧率:
```text
sequenceFps
```
有效范围为 1120 FPS,默认 30 FPS。序列帧索引根据粒子实际出生时间计算,不依赖浏览器刷新次数。
### 2.5 单粒子单骨骼原则
序列帧不会为每张图片创建独立骨骼。
运行规则为:
```text
一个粒子实例 = 一个粒子骨骼 = 一个 Sprite
多张序列图片 = 同一个 Sprite 在不同时间替换 Texture
```
因此 13 张序列图片仍只使用一根粒子骨骼,避免序列帧数量直接放大骨骼数量。
### 2.6 录制和持久化
粒子逐帧状态增加:
```text
imageFrameIndex
imageVisible
```
录制和回放会恢复:
- 当前序列帧索引;
- 当前纹理;
- 当前图片显隐;
- 资源锚点;
- 资源混合模式。
配置保存会保存序列帧文件名、预览数据和播放参数;加载时重新创建纹理。撤回快照也包含序列帧资源配置,因此选择图片、切换模式和调整播放方式均可撤回。
---
## 三、持续发射表现模式
### 3.1 两种表现
持续发射模式下增加:
| 表现 | 说明 |
|---|---|
| 常规持续 | 保持原持续发射逻辑,结束发射后等待存活粒子死亡 |
| 循环持续 | 生成可以拆分为启动、循环和结束的稳定周期动画 |
发射速率等已有参数继续共用。常规持续时长和循环持续时长分别保存,不会互相覆盖。
### 3.2 独立时长数据
常规持续继续使用:
```text
duration: number // 秒
```
循环持续新增:
```text
loopDurationFrames: number // 整数帧
```
默认循环时长为 30f,可编辑范围为 1~1800f。播放仍固定按照:
```text
30f/s
```
旧配置没有 `loopDurationFrames` 时,会把原持续秒数乘以 30 并向上取整作为迁移初值。此后两种时长完全独立。
例如:
```text
常规持续 = 1.00s
循环持续 = 54f
```
在两种表现之间切换不会改变对方的数值。
---
## 四、循环持续三段动画
### 4.1 三种状态
循环持续面板包含两个附加开关:
- 生成开始循环动画;
- 生成结束循环动画。
两个开关互斥,不能同时开启。
| 开始开关 | 结束开关 | 当前生成内容 |
|---|---|---|
| 关 | 关 | 纯循环段 |
| 开 | 关 | 启动—循环段 |
| 关 | 开 | 循环—结束段 |
### 4.2 循环衔接帧
稳定循环状态的周期边界称为“循环衔接帧”。它包含:
- 粒子骨骼集合;
- 粒子位置、旋转和缩放;
- 颜色与透明度;
- 图片资源与序列帧索引;
- 图片显隐;
- 拖尾骨骼、宽度、颜色和透明度;
- 修改器计算后的最终结果。
启动段末端、纯循环段周期边界和结束段起点使用同一套确定性计算规则。
### 4.3 启动段
启动段规则:
1. 第一帧完全为空;
2. 粒子骨骼数量为 0
3. 延迟期间保持空帧;
4. 延迟结束后开始正常生成粒子;
5. 粒子使用正常生命周期,可以在启动过程中死亡;
6. 系统自然预热到稳定循环状态;
7. 最后一帧到达循环衔接状态。
当最大粒子寿命跨越多个循环周期时,启动段会自动使用足够的预热周期,保证末端确实进入稳定循环分布。
### 4.4 纯循环段
纯循环段规则:
1. 延迟期间保持循环衔接帧;
2. 延迟结束后播放一个完整循环周期;
3. 周期长度由 `loopDurationFrames` 决定;
4. 发射事件、随机参数和资源选择在每个周期使用相同确定性序列;
5. 周期末端由真实模拟自然到达下一周期起点;
6. 不再把末帧强制替换为首帧。
这一实现保证的不只是边界两个快照一致,还保证倒数帧到结束端点、结束端点到下一周期的运动连续,不会在最后两帧突然跳变或卡顿。
### 4.5 结束段
结束段规则:
1. 第一帧为循环衔接状态;
2. 延迟期间保持该衔接状态;
3. 延迟结束后立即停止生成新粒子;
4. 已存在粒子继续按正常生命周期运行;
5. 拖尾随粒子继续更新;
6. 最后一帧所有粒子和拖尾消失。
结束段不使用发射时长作为消亡长度。长度根据衔接帧中粒子的最大剩余生命和拖尾状态自动计算。
---
## 五、稳定循环计算
### 5.1 完整周期预演
早期实现只把纯循环末帧复制为首帧。该方式虽然能让两个快照数值相同,却会使倒数帧直接跳到衔接状态,造成明显卡顿。
当前流程改为:
1. 根据循环时长计算每周期帧数;
2. 根据最大粒子寿命计算需要预演的周期数;
3. 从确定性随机种子开始模拟;
4. 每个新周期重置发射计时和随机序列;
5. 保留跨周期仍存活粒子的物理状态;
6. 预演到粒子分布稳定;
7. 记录下一完整周期及结束端点;
8. 时间轴循环时直接回放缓存结果。
### 5.2 周期随机序列
每个周期重新开始:
- 发射累计器;
- 发射计时;
- 粒子生成序号;
- 全局随机源;
- 与发射周期相关的吸附计时。
仍存活粒子不会在周期边界被重置,其运动会继续自然推进。
噪声相位、拖尾资源采样和吸附曲线方向改为使用周期内粒子生成序号,不再依赖粒子池槽位。即使粒子池复用了不同编号,也不会改变下一周期相同发射事件的视觉属性。
### 5.3 假死粒子
纯循环段需要保持粒子骨骼集合稳定。某粒子在某些帧已按正常生命周期死亡时,录制缓存不会直接删除对应骨骼,而是写入假死状态:
```text
active = true
alpha = 0
imageVisible = false
trailState = undefined
```
因此:
- 视觉上粒子已经死亡;
- 图片和拖尾均不可见;
- 逻辑上粒子骨骼仍存在;
- 周期内不会因为骨骼突然增删破坏衔接;
- 后续正常出现时仍可继续使用同一骨骼轨道。
假死只用于纯循环段。启动段和结束段仍使用正常粒子死亡逻辑。
### 5.4 缓存帧闪烁修复
循环段会在录制首帧一次性生成完整缓存。此前在全局时间轴第一次录制结束前,固定帧之间仍会调用 `update(0)` 显示粒子内部实时状态,导致画面在以下两套状态之间交替:
- 已计算好的循环缓存帧;
- 周期预演结束后的内部状态。
表现为重新计算后粒子高频闪烁,直到时间轴完整运行一遍才恢复。
当前逻辑在循环缓存存在后立即全程读取缓存,不再夹杂实时状态。
---
## 六、延迟时间
循环持续继续使用原“延迟时间”参数,三种状态均支持。
| 状态 | 延迟期间保持内容 |
|---|---|
| 启动段 | 空的第一帧 |
| 纯循环段 | 循环衔接帧 |
| 结束段 | 循环衔接帧 |
延迟结束后才开始推进对应动画。
时间轴中:
- 粒子条左侧位置继续表示延迟;
- 可以拖动粒子条调整延迟;
- 整体播放长度包含延迟帧;
- 延迟期间画布不是重复重新计算,而是保持第一帧缓存。
---
## 七、跟随功能限制
循环持续不与 Spine 骨骼跟随或路径跟随联动。原因是外部目标动画或路径进度可能不具备相同周期,无法保证粒子周期边界稳定。
进入或退出循环持续时均弹出提示:
```text
粒子跟随设置会失效并恢复默认值
```
只有用户确认后才切换。
确认后重置:
- 发射器跟随总开关;
- 跟随模式;
- 路径 ID
- Spine ID 与骨骼名;
- 跟随空间和方向;
- 路径进度曲线;
- 位移、旋转和缩放偏移。
循环持续期间跟随控件不可重新开启。切回常规持续后控件恢复可用,但不会自动恢复进入循环前的旧跟随设置。
---
## 八、时间轴规则
### 8.1 结束端点
时间轴内部保存的是快照数量,界面显示的是最后一个可选帧编号。
例如动画长度为 54f
```text
可选帧:0f54f
快照数量:55
```
因此:
- 可以拖动到第 54 帧;
- “移动到最后一帧”会到达第 54 帧;
- 后一帧按钮会正确限制在第 54 帧;
- 非循环播放会停在第 54 帧。
### 8.2 轨道显示
| 状态 | 时间轴表现 |
|---|---|
| 常规持续 | 绿色发射段 + 红色粒子存活尾段 |
| 启动段 | 延迟偏移 + 启动预热发射段 |
| 纯循环段 | 延迟偏移 + 一个完整循环周期 |
| 结束段 | 延迟偏移 + 只有粒子存在/消亡段 |
结束段不显示可调整的绿色发射时长,因为该段不会产生新粒子。
### 8.3 多系统与 Spine
每个粒子系统独立计算自己的:
- 循环时长;
- 预热周期;
- 循环衔接状态;
- 启动或结束长度;
- 延迟帧。
整体时间轴仍取以下对象的最长结束时间:
- 全部粒子系统;
- 当前加载并选择动画的 Spine 对象;
- 已存在的其他时间轴场景动画数据。
较短粒子系统到达末端后保持末帧,直到整体时间轴结束。
---
## 九、配置、预设与撤回联动
阶段十新增字段均属于粒子系统配置:
```text
streamBehavior
loopDurationFrames
generateLoopStartAnimation
generateLoopEndAnimation
imageMode
sequencePlayback
sequenceFps
sequenceFrames
```
因此它们自动纳入:
- JSON 配置保存;
- JSON 配置加载;
- `public/Preset/` 预设加载;
- 对象复制;
- 编辑器撤回快照;
- 模块重置。
加载旧配置时会补齐默认值,并处理:
-`once` 序列模式迁移为 `once-hold`
-`loop` 序列模式迁移为 `loop-forward`
- 缺少循环时长时从旧持续秒数换算初值;
- 启动和结束开关同时为真时自动保留启动段、关闭结束段;
- 缺少序列帧字段时恢复固定图片模式。
粒子逐帧缓存不写入配置。配置加载、撤回或参数修改后会根据当前确定性配置重新计算。
---
## 十、主要代码落点
| 文件 | 阶段十职责 |
|---|---|
| `src/core/particleEmitter.ts` | 序列帧纹理切换、循环配置字段、强制/停止发射控制、周期随机源重启 |
| `src/views/ImageResourceCard.vue` | 图片模式、播放模式、播放帧率、序列文件和顺序提示 |
| `src/views/ParticlePanel.vue` | 序列帧选择、循环持续面板、独立帧时长、互斥开关、跟随重置确认 |
| `src/views/Stage.vue` | 三段动画烘焙、周期预演、衔接状态、假死粒子、缓存回放与闪烁修复 |
| `src/views/Timeline.vue` | 循环轨道宽度、延迟偏移、结束段显示和末端帧选择 |
| `src/store/particleStore.ts` | 三种循环状态总帧数、多系统和 Spine 最长时间计算 |
| `src/editor/editorConfig.ts` | 序列图片数据保存、纹理重建和旧配置恢复 |
---
## 十一、验证结果
本阶段完成以下静态与构建验证:
```text
npm run build
```
结果:
- `vue-tsc -b` 通过;
- Vite 生产构建通过;
- 548 个模块完成转换;
- 无 TypeScript 错误;
- 无构建错误;
- 仅保留既有的大包体积提示。
交互修复过程中已覆盖:
- 循环时长与常规持续时长独立;
- 纯循环末端不再硬复制首帧;
- 延迟期间保持第一帧;
- 时间轴可以到达结束端点;
- 重新计算期间不再交替显示内部实时状态;
- 配置字段具备旧数据迁移默认值。
---
## 十二、阶段十最终状态
阶段十完成后,编辑器已经具备:
- 单图和序列帧混合资源;
- 多种序列播放方式;
- 确定性粒子序列帧录制;
- 常规持续与独立循环持续;
- 启动、循环、结束三段动画生成;
- 稳定周期预演;
- 假死粒子骨骼保持;
- 延迟首帧保持;
- 多系统时间轴对齐;
- 配置、预设、复制和撤回联动。
至此,阶段十“序列帧资源与循环持续动画体系”完成落盘。