《Cocos 游戏开发实战》9.3 粒子与骨骼动画

Animation 管姿态、tween 管单属性,剩下两类动效它们都做不了:金币拾取的爆散粒子、以及要换皮换动作的角色骨骼。本节讲 Cocos 3.8 的 ParticleSystem2D 与 Spine/DragonBones 骨骼,给出编辑器配置步骤、.plist 文件在编辑器里的样子,以及一张选型与性能对照表。

9.3 粒子与骨骼动画

前两节把《方块冲刺》的「动」分成了两条路:Animation 剪辑管角色姿态,tween 管单属性动效。但还有两类东西它们都做不了。

第一类是爆发式特效:金币被拾取时炸开一圈金色小点、角色加速时身后拖出残影、通关时满屏彩带。这些特效的粒子数量从几十到几百,每个粒子有自己的位置、速度、颜色、生命周期——用关键帧描述它们不现实,用 tween 一个一个补更不现实。

第二类是骨骼动画:角色要换皮肤、要复用同一套骨架做不同动作、要由美术在 Spine 里精细调曲线。这类需求下,逐帧图集(序列帧)太占内存,也改不动。

这一节把这两类交给引擎的专门系统:ParticleSystem2D 和 sp.Skeleton。

9.3.1 粒子与骨骼:两种完全不同的需求

先把概念分清楚,选型才不会错:

维度粒子系统骨骼动画
描述对象大量无固定形态的小点有骨架层级结构的角色
数据来源发射器参数(速度、角度、颜色曲线)美术用 Spine / DragonBones 导出
典型用途爆炸、拖尾、雨雪、星光角色待机/跑/跳/受击
能否换皮否是(换 skin)
内存低(同一张贴图 + 参数)中(骨架 + 图集)
运行开销与粒子数成正比与骨骼数、图集 Draw Call 相关

一句话选型:要「一堆点」用粒子,要「一具骨架」用骨骼。两者都不属于 9.1 的 Animation 剪辑,因为剪辑的本质是「给已有节点打属性关键帧」,而粒子和骨骼的内部状态由引擎自己管。

9.3.2 ParticleSystem2D:2D 粒子系统

Cocos 3.8 有两个粒子组件:3D 的 ParticleSystem 和 2D 的 ParticleSystem2D。《方块冲刺》是纯 2D 项目,用 ParticleSystem2D——它挂在 UI 节点上,和 Sprite 同一套坐标系,不受 3D 相机影响。

它的核心参数分四组:

组参数含义
发射duration / emissionRate持续多久、每秒发多少个
生命周期life / lifeVar单个粒子活多久、随机浮动范围
运动angle / angleVar / speed / speedVar初速度的方向与大小
外观startColor / endColor / startSize / endSize颜色与尺寸的起止值

带 Var 后缀的都是随机浮动量:angle = 90, angleVar = 30 表示每个粒子的初始角度在 60 到 120 度之间随机。这是粒子看起来「自然」的关键——如果所有粒子参数完全一致,它们会像一束激光而不是一团火花。

9.3.3 编辑器里配一个金币拾取粒子

这一步属于 L3——需 Cocos Creator 3.8 编辑器打开,本节不可命令行复现。目标是做 assets/prefabs/coin-burst.prefab。

  1. 层级管理器右键 → 创建 → 2D 对象 → ParticleSystem2D,命名为 CoinBurst。
  2. 在属性检查器把 Sprite Frame 设为 textures/particle-star.png(一张 16×16 的白色小星)。
  3. 设 Duration 为 0.1(一次性爆发),Emission Rate 为 0(不持续发射,靠代码触发)。
  4. 展开 Start Color,把 RGBA 设为 255, 220, 60, 255(金黄色);End Color 设为 255, 255, 255, 0(淡出到透明)。
  5. Life 设 0.5,Life Var 设 0.2。
  6. Angle 设 90,Angle Var 设 360;Speed 设 180,Speed Var 设 60。
  7. 勾选 Play On Awake 取消,勾选 Auto Remove On Finish。
  8. 把 CoinBurst 拖进 assets/prefabs/,得到 coin-burst.prefab。

操作完成后,属性面板上应显示:Total Particles 约 100,Duration 为 0.1,Emission Rate 为 0,Start Color 是金黄色,Auto Remove On Finish 勾选。把预制体拖进场景预览,你会看到点一次「播放」,节点位置炸开一圈金色小点后自动消失。

Auto Remove On Finish 很关键:粒子播完自动 destroy 节点,省得你在代码里手动回收。

9.3.4 用代码控制粒子:reset / stop

一次性粒子靠 resetSystem() 触发。下面这个组件可以过 tsc --noEmit:

import { _decorator, Component, Node, ParticleSystem2D, Prefab, instantiate } from 'cc';

const { ccclass, property } = _decorator;

@ccclass('CoinBurstSpawner')
export class CoinBurstSpawner extends Component {
  @property(Prefab)
  private burstPrefab: Prefab | null = null;

  /** 在指定世界坐标炸一次金币特效 */
  playAt(x: number, y: number): void {
    if (!this.burstPrefab) {
      return;
    }
    const fx = instantiate(this.burstPrefab);
    fx.setPosition(x, y, 0);
    this.node.addChild(fx);
    const ps = fx.getComponent(ParticleSystem2D);
    // 手动触发一次发射;节点会因 Auto Remove On Finish 自行销毁
    ps?.resetSystem();
  }
}

要点:

  • instantiate(prefab) 复制一份预制体实例,每次拾取都新建一个,互不干扰。
  • fx.getComponent(ParticleSystem2D) 拿到粒子组件后调 resetSystem()——它会清空已有粒子并重新开始发射。
  • 如果不想自动销毁,也可以保留节点、只调 stopSystem() 停止发射。

resetSystem 与 stopSystem 的区别值得记牢:stopSystem 只停止产生新粒子,已经在飞的粒子会继续飞完;resetSystem 是立刻清空并重来。做「连续拾取」时,后者才不会让上一波的残留粒子混进来。

9.3.5 粒子文件在编辑器里打开会看到什么

粒子预制体保存为 .prefab,而它的粒子参数存在节点的一个 ParticleSystem2D 组件里。需 Cocos Creator 3.8 编辑器打开,本节不可命令行复现,片段如下:

{
  "__type__": "cc.ParticleSystem2D",
  "_name": "",
  "_duration": 0.1,
  "_emissionRate": 0,
  "_life": 0.5,
  "_lifeVar": 0.2,
  "_angle": 90,
  "_angleVar": 360,
  "_speed": 180,
  "_speedVar": 60,
  "_startColor": { "__type__": "cc.Color", "r": 255, "g": 220, "b": 60, "a": 255 },
  "_endColor": { "__type__": "cc.Color", "r": 255, "g": 255, "b": 255, "a": 0 },
  "_autoRemoveOnFinish": true
}

打开编辑器后你会看到:资源管理器 assets/prefabs/ 下是 coin-burst.prefab;双击它,层级管理器只有 CoinBurst 一个根节点,属性检查器里是这个 ParticleSystem2D 组件,各项数值和上面对得上。注意颜色是 cc.Color 对象,四个通道 r/g/b/a 各占一个字段——改颜色时改的是这四个数,不是十六进制字符串。

老版本(Cocos 2.x)的粒子用 .plist 文件存参数。3.8 已经把它内联进 .prefab 的组件里,assets/ 下不会再出现 .plist。如果从网上抄到「编辑 .plist」的教程,那是对 2.x 说的,别照搬。

9.3.6 Spine 骨骼动画:sp.Skeleton

Spine 是目前 2D 骨骼动画的事实标准。美术在 Spine 编辑器里画好骨架、K 好动作,导出三样东西:.json(或 .skel)描述数据、.atlas 描述图集、一张或多张 .png 图集贴图。导入 Cocos 后,用 sp.Skeleton 组件播放。

编辑器侧(需 Cocos Creator 3.8 编辑器打开,本节不可命令行复现):

  1. 把美术导出的 hero.json、hero.atlas、hero.png 一起拖进 assets/textures/hero/。Cocos 会自动把它们识别成一套 Spine 资源。
  2. 层级管理器选中角色节点 → 添加组件 → 搜 sp.Skeleton。
  3. 把 hero 拖到组件的 Skeleton Data 字段。
  4. Default Skin 留空(用默认皮肤),Animation 字段可以先选 idle。
  5. Premultiply Alpha 按美术导出时的设置勾选,通常默认即可。

代码侧:

import { _decorator, Component, sp } from 'cc';

const { ccclass, property } = _decorator;

@ccclass('HeroSpine')
export class HeroSpine extends Component {
  @property(sp.Skeleton)
  private skeleton: sp.Skeleton | null = null;

  playRun(): void {
    if (!this.skeleton) {
      return;
    }
    // trackIndex 0 是主轨道;第三个参数 true 表示循环
    this.skeleton.setAnimation(0, 'run', true);
  }

  playHurt(): void {
    if (!this.skeleton) {
      return;
    }
    // 受击不循环,播完停在最后一帧
    this.skeleton.setAnimation(0, 'hurt', false);
  }
}

setAnimation(trackIndex, name, loop) 的三个参数:

  • trackIndex:轨道号。0 是主轨道;要做「跑动的同时播放一层武器挥砍」这种叠加,才用 1、2 号轨道。
  • name:Spine 里 K 好的动作名。
  • loop:是否循环。跑动循环,受击不循环。

还有一个 addAnimation(trackIndex, name, loop, delay)——它把动作排进队列,等当前动作播完再接上。适合「受击后自动接回跑动」这种连贯过渡。

9.3.7 DragonBones 与 SkeletalAnimation

Cocos 也内置了 DragonBones 支持,对应的组件是 SkeletalAnimation。它继承自 Animation,所以 9.1 里那套 play / crossFade / AnimationState 的用法可以复用到骨骼上——这是它相对 sp.Skeleton 的一个便利:动画状态管理是同一套 API。

项Spine (sp.Skeleton)DragonBones (SkeletalAnimation)
数据格式.json/.skel + .atlas_ske.json + _tex.json + _tex.png
播放 APIsetAnimation / addAnimationplay / crossFade(继承自 Animation)
换皮setSkinsetSkin
社区生态更广,工具链更成熟更轻量,免费版限制少

选哪个?《方块冲刺》这种小项目,看美术习惯——团队里谁熟哪个就用哪个,引擎侧两边都支持。如果美术没偏好,默认选 Spine,教程和第三方资源更多。

还有一条工程上的提醒:骨骼资源不要和粒子混在一个节点上。sp.Skeleton 是 UIRenderer,粒子的 spriteFrame 也是渲染组件,两者叠在同一节点会互相覆盖渲染属性。正确做法是骨骼挂角色根节点、粒子挂一个子节点,各管各的。

9.3.8 选型表与性能

把这一章三套表现系统合起来看:

需求用哪个关键 API
角色多属性姿态、需反复播Animation 剪辑crossFade / play
单属性一次性动效tweento / by / sequence
大量小点的爆发特效ParticleSystem2DresetSystem / stopSystem
换皮、精调曲线的角色sp.Skeleton / SkeletalAnimationsetAnimation

性能上给两条量级参考(参考值,需在你的设备上复测):

  • 一个 ParticleSystem2D 的粒子数控制在 200 以内,中低端手机上通常还能稳住 60 帧;超过这个量级,优先减粒子而不是减其他东西。
  • Spine 角色的开销主要来自图集拆分的 Draw Call,不是骨骼数。同一角色的所有插槽尽量放进一张图集,能显著减少 Draw Call。

粒子还有一个容易忽略的坑:同屏多个粒子节点会各自产生 Draw Call。金币特效一次炸一个没问题,但一屏十几个金币同时被磁铁吸走、每个都炸一次,Draw Call 会瞬间飙升。正确做法是复用一个粒子节点,或者干脆用一张序列帧图集做「假粒子」。

9.3.9 没有美术资源时:序列帧兜底

粒子图、Spine 骨架都依赖美术产出。原型阶段常常拿不到,这时候别卡住——序列帧是成本最低的兜底:让美术(或自己用工具)把一段爆炸导出成 6~8 张 PNG,拼成一张图集,用 Animation 剪辑按帧切。

做法:把图集拖进项目,用编辑器自带的图集切图功能按等分切成 8 帧,得到一个 SpriteFrame 数组;再建一条 spriteFrame 轨道的 Animation 剪辑,每 0.05 秒切一帧。这套流程和第 9.1 节的剪辑完全一致,只是轨道属性从 position 换成 spriteFrame。

序列帧的代价是内存:8 帧 128×128 的 RGBA 图集约 0.5 MB,而同等视觉的粒子可能只要 20 KB。所以它是原型的拐杖,不是终态——上线前应替换成粒子或骨骼。

9.3.10 工程视角的收口

这一节做完,工程里多了:

  • assets/prefabs/coin-burst.prefab:金币拾取粒子,Auto Remove On Finish 开启;
  • assets/scripts/game/coin-burst-spawner.ts:按坐标生成粒子;
  • assets/textures/hero/:Spine 三件套(若角色改用骨骼);
  • assets/scripts/game/hero-spine.ts:骨骼动画播放封装。

到这里,第 9 章的三块表现系统全部就位:Animation、tween、粒子与骨骼。它们各自有明确的适用边界,玩家看到的「角色在跑、金币在转、拾取时炸开」是三套系统协同的结果。

但动效只解决了一半的「反馈」——另一半是声音。金币炸开时如果没有「叮」的一声,玩家仍然觉得空。第 10 章会讲音频:怎么用 AudioSource 和音效池把三端音频管起来,怎么处理浏览器和微信小游戏的自动播放限制,以及怎么做震动和顿帧这种「打击感」的最后一层。

阅读导航:上一节:9.2 Tween 与程序动画 · 下一节:10.1 音频管理器与音效池 。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「cocos」更多文章

  1. 《Cocos 游戏联机实战》9.3 延迟补偿与体验权衡
  2. 《Cocos 游戏联机实战》9.2 网络状态 UI 与提示
  3. 《Cocos 游戏联机实战》9.1 同步表现与插值动画