强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Pixi.js 游戏开发教程 / Pixi.js 粒子系统(@pixi/particle-emitter)

15. 粒子系统(@pixi/particle-emitter)

概述

粒子系统用于创建火焰、爆炸、烟雾、星光等视觉效果。@pixi/particle-emitter 是 Pixi.js 生态中最流行的粒子库,支持丰富的配置选项和高性能渲染。

安装与基本使用

npm install @pixi/particle-emitter
import { Application, Container } from 'pixi.js';
import { Emitter } from '@pixi/particle-emitter';

const app = new Application();
await app.init({ width: 800, height: 600 });

const particleContainer = new Container();
app.stage.addChild(particleContainer);

// 基础发射器配置
const emitter = new Emitter(particleContainer, {
    lifetime: { min: 0.5, max: 1.5 },
    frequency: 0.01,        // 每 0.01 秒发射一个粒子
    emitterLifetime: -1,    // -1 = 永久发射
    maxParticles: 500,
    pos: { x: 400, y: 300 },
    behaviors: [
        {
            type: 'alpha',
            config: {
                alpha: {
                    list: [
                        { value: 1, time: 0 },
                        { value: 0, time: 1 },
                    ],
                },
            },
        },
        {
            type: 'scale',
            config: {
                scale: {
                    list: [
                        { value: 0.5, time: 0 },
                        { value: 0.1, time: 1 },
                    ],
                },
            },
        },
        {
            type: 'moveSpeed',
            config: {
                speed: {
                    list: [
                        { value: 100, time: 0 },
                        { value: 20, time: 1 },
                    ],
                },
            },
        },
        {
            type: 'color',
            config: {
                color: {
                    list: [
                        { value: 'ff6600', time: 0 },
                        { value: 'ff0000', time: 0.5 },
                        { value: '333333', time: 1 },
                    ],
                },
            },
        },
        {
            type: 'textureSingle',
            config: {
                texture: '/image/particle.png',
            },
        },
    ],
});

// 更新循环
app.ticker.add((ticker) => {
    emitter.update(ticker.deltaMS * 0.001);
});

发射器配置详解

发射类型

类型配置说明
点发射spawnType: 'point'从单点发射(默认)
线发射spawnType: 'line'沿线段发射
圆发射spawnType: 'circle'从圆周发射
矩形发射spawnType: 'rect'从矩形区域发射
环形发射spawnType: 'ring'从环形区域发射
// 矩形区域发射(如篝火上方)
{
    type: 'spawnShape',
    config: {
        type: 'rect',
        data: { x: -20, y: -5, w: 40, h: 10 },
    },
}

// 环形发射(如魔法光环)
{
    type: 'spawnShape',
    config: {
        type: 'ring',
        data: {
            x: 0, y: 0,
            minRadius: 40,
            maxRadius: 50,
        },
    },
}

速度与方向

{
    type: 'moveSpeed',
    config: {
        speed: {
            list: [
                { value: 200, time: 0 },
                { value: 0, time: 1 },
            ],
        },
        minMult: 0.5, // 速度随机倍率最小值
    },
},
{
    type: 'moveAcceleration',
    config: {
        accel: { x: 0, y: 500 }, // 重力下坠
        minMult: 1,
    },
},
{
    type: 'rotation',
    config: {
        minStart: 0,
        maxStart: 360,
        minEnd: 0,
        maxEnd: 360,
    },
},

生命周期、颜色、缩放

// 生命周期:每个粒子存活时间
{ lifetime: { min: 0.3, max: 1.0 } }

// 颜色渐变
{
    type: 'color',
    config: {
        color: {
            list: [
                { value: 'ffffff', time: 0 },
                { value: '00aaff', time: 0.5 },
                { value: '000033', time: 1 },
            ],
        },
    },
}

// 缩放渐变
{
    type: 'scale',
    config: {
        scale: {
            list: [
                { value: 1.0, time: 0 },
                { value: 0.0, time: 1 },
            ],
        },
        minMult: 0.5,
    },
}

粒子池(复用)

@pixi/particle-emitter 内部使用粒子池,但你需要注意纹理管理:

// 共享纹理提高性能
import { Texture } from 'pixi.js';

const sharedParticleTex = Texture.from('/image/particle.png');

// 多个发射器共享同一纹理
const fireEmitter = new Emitter(container, {
    // ...
    behaviors: [
        { type: 'textureSingle', config: { texture: sharedParticleTex } },
    ],
});

const smokeEmitter = new Emitter(container, {
    // ...
    behaviors: [
        { type: 'textureSingle', config: { texture: sharedParticleTex } },
    ],
});

💡 提示:所有粒子使用相同的白色圆形纹理(白圆),通过颜色和缩放变化来表现不同效果,这样只需加载一张纹理。

火焰效果

const fireConfig = {
    lifetime: { min: 0.3, max: 0.8 },
    frequency: 0.005,
    maxParticles: 300,
    pos: { x: 0, y: 0 },
    behaviors: [
        {
            type: 'alpha',
            config: {
                alpha: {
                    list: [
                        { value: 0.8, time: 0 },
                        { value: 0.0, time: 1 },
                    ],
                },
            },
        },
        {
            type: 'scale',
            config: {
                scale: {
                    list: [
                        { value: 0.8, time: 0 },
                        { value: 0.2, time: 1 },
                    ],
                },
            },
        },
        {
            type: 'color',
            config: {
                color: {
                    list: [
                        { value: 'ffff00', time: 0 },
                        { value: 'ff6600', time: 0.3 },
                        { value: 'cc0000', time: 0.7 },
                        { value: '333333', time: 1 },
                    ],
                },
            },
        },
        {
            type: 'moveSpeed',
            config: {
                speed: {
                    list: [
                        { value: 80, time: 0 },
                        { value: 20, time: 1 },
                    ],
                },
                minMult: 0.5,
            },
        },
        {
            type: 'rotation',
            config: { minStart: -90, maxStart: -90, minEnd: -90, maxEnd: -90 },
        },
        {
            type: 'textureSingle',
            config: { texture: '/image/particle.png' },
        },
        {
            type: 'spawnShape',
            config: {
                type: 'rect',
                data: { x: -15, y: -5, w: 30, h: 10 },
            },
        },
    ],
};

爆炸效果

function createExplosion(container, x, y) {
    const explosion = new Emitter(container, {
        lifetime: { min: 0.2, max: 0.6 },
        frequency: 0.001,      // 快速发射
        emitterLifetime: 0.1,  // 只发射 0.1 秒
        maxParticles: 100,
        pos: { x, y },
        behaviors: [
            {
                type: 'alpha',
                config: {
                    alpha: {
                        list: [
                            { value: 1.0, time: 0 },
                            { value: 0.0, time: 1 },
                        ],
                    },
                },
            },
            {
                type: 'scale',
                config: {
                    scale: {
                        list: [
                            { value: 1.5, time: 0 },
                            { value: 0.0, time: 1 },
                        ],
                    },
                },
            },
            {
                type: 'moveSpeed',
                config: {
                    speed: {
                        list: [
                            { value: 400, time: 0 },
                            { value: 0, time: 1 },
                        ],
                    },
                },
            },
            {
                type: 'color',
                config: {
                    color: {
                        list: [
                            { value: 'ffffff', time: 0 },
                            { value: 'ffaa00', time: 0.2 },
                            { value: 'ff4400', time: 0.5 },
                            { value: '444444', time: 1 },
                        ],
                    },
                },
            },
            {
                type: 'textureSingle',
                config: { texture: '/image/particle.png' },
            },
        ],
    });

    // 发射完成后清理
    setTimeout(() => {
        explosion.destroy();
    }, 2000);

    return explosion;
}

烟雾效果

const smokeConfig = {
    lifetime: { min: 2.0, max: 4.0 },
    frequency: 0.05,
    maxParticles: 100,
    behaviors: [
        {
            type: 'alpha',
            config: {
                alpha: {
                    list: [
                        { value: 0.0, time: 0 },
                        { value: 0.3, time: 0.2 },
                        { value: 0.0, time: 1 },
                    ],
                },
            },
        },
        {
            type: 'scale',
            config: {
                scale: {
                    list: [
                        { value: 0.3, time: 0 },
                        { value: 2.0, time: 1 },
                    ],
                },
            },
        },
        {
            type: 'moveSpeed',
            config: {
                speed: {
                    list: [
                        { value: 30, time: 0 },
                        { value: 10, time: 1 },
                    ],
                },
            },
        },
        {
            type: 'moveAcceleration',
            config: {
                accel: { x: 10, y: -20 }, // 向上飘散
            },
        },
        {
            type: 'textureSingle',
            config: { texture: '/image/smoke.png' },
        },
    ],
};

粒子与游戏事件联动

class ParticleEffectsManager {
    private container: Container;
    private emitters: Emitter[] = [];

    constructor(parent: Container) {
        this.container = new Container();
        parent.addChild(this.container);
    }

    // 拾取物品闪光
    pickupEffect(x: number, y: number, color: string) {
        const emitter = new Emitter(this.container, {
            lifetime: { min: 0.2, max: 0.5 },
            frequency: 0.001,
            emitterLifetime: 0.05,
            maxParticles: 30,
            pos: { x, y },
            behaviors: [
                { type: 'alpha', config: { alpha: { list: [
                    { value: 1, time: 0 }, { value: 0, time: 1 },
                ]}}},
                { type: 'scale', config: { scale: { list: [
                    { value: 0.5, time: 0 }, { value: 0, time: 1 },
                ]}}},
                { type: 'moveSpeed', config: { speed: { list: [
                    { value: 150, time: 0 }, { value: 0, time: 1 },
                ]}}},
                { type: 'color', config: { color: { list: [
                    { value: color, time: 0 }, { value: 'ffffff', time: 1 },
                ]}}},
                { type: 'textureSingle', config: { texture: '/image/spark.png' } },
            ],
        });
        this.scheduleEmitterCleanup(emitter);
    }

    // 受伤闪红
    damageEffect(x: number, y: number) {
        this.pickupEffect(x, y, 'ff0000');
    }

    update(dt: number) {
        for (const emitter of this.emitters) {
            emitter.update(dt);
        }
    }

    private scheduleEmitterCleanup(emitter: Emitter) {
        this.emitters.push(emitter);
        setTimeout(() => {
            emitter.destroy();
            this.emitters = this.emitters.filter(e => e !== emitter);
        }, 3000);
    }
}

GPU 粒子性能

优化手段说明
使用 ParticleContainerPixi.js 的专用粒子容器,性能更高
限制最大粒子数maxParticles 设合理上限
共享纹理所有粒子使用同一纹理
降低发射频率frequency 不必太小
复用发射器使用对象池而非频繁创建/销毁
import { ParticleContainer } from 'pixi.js';

// ParticleContainer 不支持 tint、滤镜等,但性能更好
const particles = new ParticleContainer(10000, {
    scale: true,
    position: true,
    alpha: true,
    rotation: true,
});

粒子编辑器工具

工具说明链接
Pixi Particles Editor在线编辑器https://pixi-particles.clevercanyon.com
Particle Designer桌面工具GitHub 开源
手写 JSON最灵活直接编写配置对象

💡 提示:推荐先用在线编辑器调参预览,导出 JSON 后在代码中使用。

自定义粒子行为

// 自定义行为:粒子颜色随速度变化
class SpeedColorBehavior {
    constructor(particle) {
        this.particle = particle;
    }

    init(firstInit) {
        // 初始化时调用
    }

    update(particle) {
        const speed = Math.sqrt(
            particle.vx * particle.vx + particle.vy * particle.vy
        );
        const t = Math.min(1, speed / 300);
        // 速度越快越红
        particle.tint = lerpColor(0x00aaff, 0xff0000, t);
    }

    destroy() {}
}

function lerpColor(a, b, t) {
    const ar = (a >> 16) & 0xff;
    const ag = (a >> 8) & 0xff;
    const ab = a & 0xff;
    const br = (b >> 16) & 0xff;
    const bg = (b >> 8) & 0xff;
    const bb = b & 0xff;
    const r = Math.round(ar + (br - ar) * t);
    const g = Math.round(ag + (bg - ag) * t);
    const bl = Math.round(ab + (bb - ab) * t);
    return (r << 16) | (g << 8) | bl;
}

扩展阅读