外观
Stage05|Performance
Lesson068|Label、Animation、Particle 的常见性能问题
Tags: #Creator2.x #Stage05 #Label #Animation #ParticleDifficulty: ⭐⭐⭐☆☆
真实问题:战斗 HUD 单项都不慢,合在一起为什么掉帧
单测 100 个 Label、100 个 Animation、1000 个 Particle 都勉强可接受;真实战斗中三者同时在峰值发生:
text
伤害数字生成与改字
技能节点播放 Animation
粒子大量发射与透明叠加
→ CPU、GC、DrawCall、Overdraw 同时上升性能预算必须按真实并发组合验收。
一、本课目标
本课不把 Label、Animation、Particle 简单归类为“性能差”,而是分析它们在什么使用方式下容易产生高成本。
二、Label 的常见成本
Label 变化时可能涉及:
text
字符串处理
字体图集或字形查找
顶点/UV 更新
布局尺寸变化
渲染批次变化高风险用法:
- 每帧给大量 Label 赋相同字符串。
- 动态字体频繁改变内容。
- 长文本不断触发换行和布局。
- 数字滚动创建大量临时字符串。
优化方向:只在值变化时更新、缓存格式、限制刷新频率、减少不可见 Label。
三、Animation 的常见成本
动画可能同时更新:
text
位置、旋转、缩放、颜色、激活状态、事件大量动画对象会增加采样、曲线计算和属性写入。需要检查:
- 是否真的需要逐帧动画。
- 不可见对象是否仍在播放。
- 多个动画是否写同一属性。
- 动画结束后是否清理引用和回调。
四、Particle 的常见成本
粒子系统通常包含:
text
粒子生成
生命周期更新
顶点数据更新
透明混合
Overdraw全屏、大尺寸、半透明、长生命周期粒子特别容易增加 GPU Fill Rate 和 CPU 更新成本。
五、案例:战斗数字和特效
text
每次命中创建一个 Label
→ 播放缩放 Tween
→ 生成粒子
→ 透明淡出
→ 销毁高频时可能同时造成:
- JS 分配和 GC。
- 节点创建销毁。
- Label 更新。
- 粒子 Overdraw。
- DrawCall 和纹理切换。
应分别采样,而不是只关闭某一个效果。
六、常见误区
误区一:把 Label 换成 Sprite 就一定更快
Sprite 数量、纹理、DrawCall 和更新频率也会改变成本。
误区二:不可见动画自动没有成本
需要确认节点、组件和动画是否真的停止更新。
误区三:粒子越少越好
应根据视觉目标减少粒子数、生命周期、覆盖面积和采样次数。
误区四:性能问题只属于 CPU 或 GPU 其中一边
Label、Animation、Particle 常常同时影响脚本、数据更新、提交和像素处理。
七、练习与答案
- Label 频繁更新可能触发哪些工作?
- 为什么不可见动画仍需检查 enabled 和 activeInHierarchy?
- 粒子为什么容易引起 Overdraw?
- 战斗飘字应该从哪些维度采样?
答案:
- 字符串、字形/图集、顶点、布局和渲染数据更新。
- 不可见不代表调度一定停止。
- 粒子通常是多层半透明片元覆盖。
- 对象创建、GC、Label 更新、粒子 CPU/GPU、DrawCall 和内存。
Creator 2.4.x 综合实验:三个负载旋钮
ComponentCostProbe.ts
ts
const { ccclass, property } = cc._decorator;
@ccclass
export default class ComponentCostProbe extends cc.Component {
@property(cc.Node)
labelGroup: cc.Node = null;
@property(cc.Node)
animationGroup: cc.Node = null;
@property(cc.Node)
particleGroup: cc.Node = null;
setLabels(active: boolean) {
this.labelGroup.active = active;
}
setAnimations(active: boolean) {
this.animationGroup.active = active;
}
setParticles(active: boolean) {
this.particleGroup.active = active;
}
}实验矩阵
text
只 Label
只 Animation
只 Particle
Label + Animation
Animation + Particle
三者同时每组记录 CPU、DrawCall、p99、粒子覆盖和内存。组合成本可能不是简单相加,因为 GC、热降频和批次交错会出现。
Label 的重点证据
- 每秒字符串写入次数;
- 动态字符集合;
- Cache Mode;
- 节点尺寸与 Layout;
- 同时可见数量;
- 对象池内 Label 是否仍持有字形/资源。
伤害数字常只需固定数字字符集、固定宽度、有限数量和低频更新。
Animation 的重点证据
- 活跃 Animation 数;
- 每条 Clip 的曲线/属性数;
- 是否动画大父树 Transform;
- 不可见时是否仍播放;
- 同一属性是否同时被 Tween/Layout 写入;
- 事件帧回调数量。
动画节点 inactive/离屏不等于所有自定义动画系统自动暂停。
Particle 的重点证据
- 最大活跃粒子;
- 发射率和生命周期;
- Quad 尺寸;
- 纹理透明留白;
- Blend 与材质;
- 是否离屏仍模拟;
- 同屏多特效峰值。
真实项目故障:暴击连击时每次都卡
一次暴击触发 30 个伤害 Label、6 套骨骼动画、10 个粒子发射器和震屏。优化按优先级:
- 合并同类事件,同帧限制最大伤害数字;
- 伤害数字使用容量有限的池并完整 reset;
- 粒子按设备档位限制发射;
- 不可见/远处动画降频;
- 相同效果资源共享;
- 峰值脚本跨帧调度但不改变战斗语义;
- 回放相同战斗数据比较 p99。
诊断与练习答案
- Animation 数量少为何仍可能贵? 单个 Clip 可驱动大子树、多属性或复杂骨骼,成本看工作量而非组件数。
- Particle DrawCall 低为何仍慢? 大量重叠透明 Quad 可造成 Fill Rate。
- Label 对象池为何仍要限制容量? 池节点、组件和资源继续占内存,过大峰值不应永久保留。
- 为什么要测组合峰值? 实际玩法同时触发多个系统,资源竞争、GC 和热效应会放大长帧。
组合实验的回归标准
只关闭某个系统看帧率,容易把“少了功能”误判成优化。每次组合实验应同时记录:
text
功能触发次数
可见 Label 数
活跃 Animation 数
活跃粒子数
CPU p95/p99
GPU/DrawCall
纹理与 JS 内存例如把粒子数量减半后,除了帧时间,还要确认:
- 伤害反馈仍然可辨认;
- 粒子生命周期和对象池回收正确;
- 动画事件不会提前/重复;
- Label 文本没有因复用显示旧值;
- 低端档与高端档降级规则一致。
分阶段验收
- 单独开启 Label,确认文本和字体回归。
- 单独开启 Animation,确认曲线、事件和停止。
- 单独开启 Particle,确认发射、回收和透明效果。
- 开启三者组合,确认峰值。
- 快速开关页面和切场景,确认监听、Tween、粒子和池状态清理。
八、本课总结
text
专项组件没有绝对好坏,成本取决于数量、频率、可见区域和资源配置。
优化时要拆开 CPU、GPU、内存和生命周期成本。九、下一课预告
text
Lesson069|启动速度、首屏时间与分阶段加载