mcpField
P
pixijs-scene-particle-container

pixijs-scene-particle-container

github.com/pixijs开发3585.9k 次安装4 周前更新
代码仓库

关于 pixijs-scene-particle-container

面向 PixiJS 开发者的技能,介绍如何用 ParticleContainer 批量渲染共享纹理的轻量粒子。内容涵盖粒子管理、GPU 更新设置,以及这种专用容器的功能取舍。

主要功能

  1. 01批量渲染共享纹理的粒子
  2. 02创建和批量管理粒子
  3. 03配置动态 GPU 属性
  4. 04自定义粒子着色器
  5. 05在容器级设置色调、透明度和混合模式

适用场景

  1. 01渲染包含大量精灵的粒子效果
  2. 02使用共享纹理制作弹幕
  3. 03通过标记动画属性优化更新
  4. 04为整批粒子调整色调或淡出

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/pixijs/pixijs-skills --skill pixijs-scene-particle-container

安装后在 Claude Code 中输入/pixijs-scene-particle-container确认已加载。

详细介绍

ParticleContainer 是一种专用容器,可通过一次绘制调用渲染数百到数万个轻量级精灵。它适用于粒子效果、子弹阵列,或任何需要大量外观相似对象且希望尽量减少单对象开销的场景。粒子共用一个基础纹理,并且可使用的变换有限;它们不是完整的 Container 子对象。

假设你已熟悉 pixijs-scene-core-concepts。从另一个角度来说,ParticleContainer 是一种特殊的叶节点:它将 Particle 实例存放在自己的 particleChildren 数组中,并拒绝普通的 PixiJS 子对象。请使用 addParticle,而非 addChild;如果需要将整个 ParticleContainer 与其他场景对象编组,请将它包装在一个 Container 中。

Particle API 在 v8 中是新增的,但已稳定,可用于生产环境。

快速入门

const texture = await Assets.load("particle.png");

const container = new ParticleContainer({
  texture,
  boundsArea: new Rectangle(0, 0, app.screen.width, app.screen.height),
  dynamicProperties: {
    position: true,
    rotation: false,
    color: false,
  },
});

for (let i = 0; i < 10000; i++) {
  container.addParticle(
    new Particle({
      texture,
      x: Math.random() * app.screen.width,
      y: Math.random() * app.screen.height,
    }),
  );
}

app.stage.addChild(container);

相关技能: pixijs-scene-core-concepts(场景图基础知识)、pixijs-scene-sprite(需要为每个对象使用完整功能时)、pixijs-assets(共享纹理、图集)、pixijs-performance(批处理、纹理优化)、pixijs-scene-container(与其他显示对象一起包装)。

构造函数选项

ParticleContainerOptions

所有 Container 选项(position、scale、tint、label、filters、zIndex 等)在此处同样有效,详见 skills/pixijs-scene-core-concepts/references/constructor-options.md。请注意,children 已省略:请改用 particles。

选项类型默认值说明
textureTexturenull所有粒子共用的基础纹理。如果省略,容器会回退到添加的第一个粒子的纹理;每个粒子都必须共用相同的基础纹理源。
particlesT[][]初始 Particle(或 IParticle)实例数组。相当于对每个实例调用 addParticle,但会跳过逐次调用时的视图更新。
dynamicPropertiesParticleProperties{ vertex: false, position: true, rotation: false, uvs: false, color: false }标记每帧重新上传到 GPU 的粒子属性。默认只有 position 是动态的;请将需要进行动画处理的属性标记为动态,其余属性保持静态以提升速度。
roundPixelsbooleanfalse将粒子位置四舍五入到最近的像素。对于像素艺术风格,这能让渲染更清晰,但会牺牲平滑的亚像素运动。
shaderShader默认粒子着色器替换默认粒子着色器。自定义着色器必须声明 aPosition、aUV、aColor,以及通过 dynamicProperties 启用的任何仅动态属性。

boundsArea 继承自 Container,但对于 ParticleContainer 来说实际上是必需的:为了提升性能,容器默认返回空边界 (0, 0, 0, 0);因此,在启用剔除时,如果没有设置 boundsArea,容器会被当作不可见对象剔除,而且 containsPoint 始终判定为未命中。

ParticleOptions

Particle 是轻量级结构体,而非 Container 子类,因此 ContainerOptions 中的字段均不适用。完整选项列表如下:

选项类型默认值描述
textureTexture—必填。用于渲染此粒子的纹理。同一 ParticleContainer 中的所有粒子必须共享相同的基础纹理源。
xnumber0容器局部空间中的 X 坐标。
ynumber0容器局部空间中的 Y 坐标。
scaleXnumber1水平缩放系数。
scaleYnumber1垂直缩放系数。
anchorXnumber00–1 范围内的水平锚点;0 表示左侧,0.5 表示中心,1 表示右侧。
anchorYnumber00–1 范围内的垂直锚点;0 表示顶部,0.5 表示中心,1 表示底部。
rotationnumber0旋转角度,单位为弧度。
tintColorSource0xffffff色调颜色,可使用十六进制数或 CSS 颜色字符串。与 alpha 合并后写入内部 color 字段。
alphanumber1透明度(0–1)。超出范围的值会被限制在范围内。与 tint 合并后写入内部 color 字段。

构造函数也接受一个直接传入的 Texture 作为唯一参数(new Particle(texture)),这等同于使用上述默认值调用 new Particle({ texture })。

Particle.defaultOptions 是一个静态对象,可重新赋值以全局更改默认值;请参见下文的“创建粒子”部分。

核心模式

创建粒子
const particle = new Particle({
  texture,
  x: 100,
  y: 200,
  scaleX: 0.5,
  scaleY: 0.5,
  anchorX: 0.5,
  anchorY: 0.5,
  rotation: Math.PI / 4,
  tint: 0xff0000,
  alpha: 0.8,
});

container.addParticle(particle);

Particle 是具有扁平数值字段的轻量级结构体:x、y、scaleX、scaleY、anchorX、anchorY、rotation、color、texture。它还提供 tint(十六进制/CSS 颜色)和 alpha(0-1)设置器,这些设置器会将值合并到内部 color 字段中。它没有变换层级、事件或滤镜。

你可以直接传入一个 Texture 作为唯一参数:new Particle(texture)。

重写 Particle.defaultOptions 可全局更改默认值:

Particle.defaultOptions = {
  ...Particle.defaultOptions,
  anchorX: 0.5,
  anchorY: 0.5,
};
使用 particles 选项预先填充
const particles = Array.from(
  { length: 10000 },
  () =>
    new Particle({
      texture,
      x: Math.random() * 800,
      y: Math.random() * 600,
    }),
);

const container = new ParticleContainer({
  texture,
  boundsArea: new Rectangle(0, 0, 800, 600),
  particles,
});

在构造函数中传入 particles,等同于创建一个空容器并逐个调用 addParticle,但可以避免每次调用都更新视图。

动态属性与静态属性及 update()
const container = new ParticleContainer({
  dynamicProperties: {
    rotation: true,
  },
});

dynamicProperties 控制每帧重新上传到 GPU 的粒子属性。ParticleContainer.defaultOptions.dynamicProperties 的默认值为:

{ vertex: false, position: true, rotation: false, uvs: false, color: false }

你只需覆盖正在动画的属性,其余属性会沿用默认值(位置为动态,其他属性均为静态)。总共有五种属性:

  • vertex:缩放/锚点顶点
  • position:位置
  • rotation:旋转
  • uvs:纹理坐标(用于帧切换的粒子)
  • color:色调和透明度

只标记正在动画的属性;静态属性开销更低。如果在运行时更改静态属性,请调用 container.update() 重新上传:

container.particleChildren.forEach((p) => {
  p.tint = 0x00ff00;
});
container.update();
对 particleChildren 执行批量操作
// Bulk add
const batch = [];
for (let i = 0; i < 5000; i++) {
  batch.push(
    new Particle({ texture, x: Math.random() * 800, y: Math.random() * 600 }),
  );
}
container.particleChildren.push(...batch);
container.update();

// Bulk remove
container.particleChildren.length = 0;
container.update();

addParticle、addParticleAt、removeParticle、removeParticleAt 和 removeParticles 每次调用都会触发视图更新。对于大型批量操作,直接操作数组并只调用一次 update() 会更快。

纹理和着色器选项

ParticleContainerOptions 中的 texture 是可选的。如果省略,容器会回退到添加的第一个粒子的纹理;之后的每个粒子都必须共享相同的基础纹理源。如果要预先指定图集,或第一个粒子可能在运行过程中发生变化,请显式设置:

const container = new ParticleContainer({ texture });

shader 可用于将默认粒子着色器替换为任意 Shader 实例。自定义着色器必须声明粒子管线上传的属性(aPosition、aUV、aColor,以及通过 dynamicProperties 启用的任何仅动态属性)。可用于自定义混合运算、距离场精灵或非标准效果:

const container = new ParticleContainer({ texture, shader: myCustomShader });
淡化或着色整个粒子系统
const burst = new ParticleContainer({ texture, particles });
const layer = new Container();
layer.addChild(burst);

app.ticker.add(() => {
  layer.alpha -= 0.01; // fades every particle
});
burst.tint = 0x66ccff; // tints every particle

容器的 alpha、tint 和 blendMode 会像应用于精灵一样级联到粒子,因此你可以淡化或重新着色整个系统,而无需操作 particleChildren。

限制

ParticleContainer 有意牺牲部分功能以提升速度:

  • 不支持滤镜、遮罩或单个粒子的混合模式。混合模式应用于容器级别:在 ParticleContainer 上设置 blendMode,或让它从父级 Container 继承(默认值 "inherit" 会解析为祖先的混合模式,与 Sprite 和 Mesh 相同)。整个粒子批次共享一种混合模式。容器或任意祖先上的 alpha 和 tint 会与每个粒子自身的 alpha/tint 相乘。
  • 粒子不支持嵌套子级。
  • 不会自动计算边界。
  • 所有粒子都必须共享相同的基础纹理源(支持图集;不支持多个互不相关的纹理)。
  • 支持通过 shader 选项使用自定义着色器。
容器方法迁移

ParticleContainer 使用单独的子级管理 API,以优化 GPU 缓冲区更新。在 ParticleContainer 上调用标准 Container 子级方法会抛出异常。

标准 Container 方法ParticleContainer 对应方法
addChild(child)addParticle(particle)
removeChild(child)removeParticle(particle)
addChildAt(child, index)addParticleAt(particle, index)
removeChildAt(index)removeParticleAt(index)
removeChildren(begin, end)removeParticles(begin, end)
getChildAt(index)直接访问 container.particleChildren[index]
swapChildren()不可用
reparentChild()不可用

常见错误

[CRITICAL] 向 ParticleContainer 添加精灵

错误:

const container = new ParticleContainer();
const sprite = new Sprite(texture);
container.addChild(sprite);

正确:

const container = new ParticleContainer();
const particle = new Particle(texture);
container.addParticle(particle);

ParticleContainer 不接受 Sprite 子对象。addChild 会抛出错误。粒子必须是 Particle 实例(或任何实现了 IParticle 的对象),并通过 addParticle 添加。这是相较于 v7 的全面重构;在 v7 中,ParticleContainer 接受 Sprite 子对象。

[HIGH] 未在 ParticleContainer 上设置 boundsArea

错误:

const container = new ParticleContainer();
// bounds is always (0, 0, 0, 0) — culling and hit testing fail

正确:

const container = new ParticleContainer({
  boundsArea: new Rectangle(0, 0, 800, 600),
});

为了提高性能,ParticleContainer 默认返回空边界 (0, 0, 0, 0)。如果未设置 boundsArea,启用剔除时容器会被视为不可见并被剔除,且 containsPoint 总会判定为未命中。请将 boundsArea 设置为粒子所在的区域。

[HIGH] 使用 children 而不是 particleChildren

错误:

container.addParticle(new Particle(texture));
console.log(container.children.length); // 0

正确:

container.addParticle(new Particle(texture));
console.log(container.particleChildren.length); // 1

粒子存储在 particleChildren 数组中,而非 children 中。ParticleContainer 的标准 Container.children 数组为空。所有粒子枚举、计数和操作都必须使用 particleChildren 以及 *Particle 方法。

[MEDIUM] 不要将 ParticleContainer 用作普通容器

ParticleContainer 包含的是粒子,而不是显示对象。如果需要将粒子系统与背景精灵或 UI 覆盖层组合在一起,请将 ParticleContainer 本身包装在一个普通的 Container 中:

const world = new Container();
world.addChild(backgroundSprite, particleContainer, uiLayer);

API 参考

评论

支持 Markdown