mcpField
P
particles

particles

github.com/phaserjs开发40.6k889 次安装10 周前更新
代码仓库

关于 particles

面向 Phaser 4 开发者的粒子系统技能,介绍 ParticleEmitter 的创建与配置,以及粒子的发射、更新和销毁控制。涵盖多种发射模式、区域与自定义粒子逻辑。

主要功能

  1. 01创建和配置 ParticleEmitter
  2. 02流式发射与爆发模式
  3. 03生命周期属性与颜色插值
  4. 04重力井及发射、销毁区域
  5. 05跟随游戏对象与粒子回调
  6. 06自定义粒子类和处理器

适用场景

  1. 01制作持续喷射或瞬间爆发效果
  2. 02在指定几何区域生成或清除粒子
  3. 03让粒子效果跟随游戏对象
  4. 04实现逐帧自定义粒子行为

安装

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

$npx skills add https://github.com/phaserjs/phaser --skill particles

安装后在 Claude Code 中输入/particles确认已加载。

附带文件references/REFERENCE.md

详细介绍

粒子系统

在 Phaser 4 中创建和控制粒子效果——ParticleEmitter 的创建与配置、emitter ops(值格式)、重力井、发射区域和消亡区域、流动模式与爆发模式、跟随游戏对象,以及粒子回调。

关键源码路径: src/gameobjects/particles/ 相关技能: ../sprites-and-images/SKILL.md, ../loading-assets/SKILL.md

快速开始

// In a Scene's create() method:

// Basic continuous emitter (flow mode)
const emitter = this.add.particles(400, 300, 'flares', {
    frame: 'red',
    speed: 200,
    lifespan: 2000,
    scale: { start: 1, end: 0 },
    alpha: { start: 1, end: 0 },
    gravityY: 150
});

// One-shot burst (explode mode)
const burst = this.add.particles(400, 300, 'flares', {
    frame: 'blue',
    speed: { min: 100, max: 300 },
    lifespan: 1000,
    scale: { start: 0.5, end: 0 },
    emitting: false   // don't auto-start
});
burst.explode(20);    // emit 20 particles at once

核心概念

ParticleEmitter

ParticleEmitter 继承自 GameObject,可直接添加到显示列表中。它既是游戏对象(可定位、可缩放、可遮罩),也是 emitter 本身。没有独立的管理器——this.add.particles() 返回一个 ParticleEmitter 实例。

工厂方法签名:

this.add.particles(x, y, texture, config);
// x, y: world position (both optional, default 0)
// texture: string key or Texture instance
// config: ParticleEmitterConfig object (optional, can call setConfig later)

Mixins: AlphaSingle、BlendMode、Depth、Lighting、Mask、RenderNodes、ScrollFactor、Texture、Transform、Visible。因此你可以调用 setPosition()、setScale()、setDepth()、setBlendMode()、setMask()、setScrollFactor() 等。

Particle

由其 emitter 拥有的轻量级对象。关键属性:x、y、velocityX/Y、accelerationX/Y、scaleX/Y、alpha、angle、rotation、tint、life(总毫秒数)、lifeCurrent(剩余毫秒数)、lifeT(0-1 归一化值)、bounce、delayCurrent、holdCurrent。粒子在内部池化——你无需手动创建它们。

EmitterOp 值格式

大多数配置属性(speed、scale、alpha、angle、x、y 等)接受灵活的值格式:

x: 400                                        // static value
x: [100, 200, 300, 400]                       // random pick from array
x: { min: 100, max: 700 }                     // random float in range
x: { min: 100, max: 700, int: true }          // random integer
x: { random: [100, 700] }                     // random integer shorthand
scale: { start: 0, end: 1 }                   // ease over lifetime (default linear)
scale: { start: 0, end: 1, ease: 'bounce.out' }  // custom ease
scale: { start: 4, end: 0.5, random: true }   // random start, ease to end
x: { values: [50, 500, 200, 800], interpolation: 'catmull' }  // interpolation
x: { steps: 32, start: 0, end: 576 }          // stepped sequential
x: { steps: 32, start: 0, end: 576, yoyo: true }  // stepped with yoyo
x: {                                           // custom callbacks
    onEmit: (particle, key, t, value) => value,
    onUpdate: (particle, key, t, value) => value
}
x: (particle, key, t, value) => value + 50    // emit-time callback shorthand

仅发射(无 onUpdate):angle、delay、hold、lifespan、quantity、speedX、speedY。 发射 + 更新(支持 start/end、onUpdate):accelerationX/Y、alpha、bounce、maxVelocityX/Y、moveToX/Y、rotate、scaleX/Y、tint、x、y。

流动模式与爆发模式(Burst)

流动模式(frequency >= 0):每 frequency 毫秒发射 quantity 个粒子。默认为 frequency: 0(每帧)且 emitting: true。

爆发模式(frequency = -1):一次性批量发射,然后停止。

emitter.flow(100, 5);           // 5 particles every 100ms
emitter.flow(100, 5, 50);       // auto-stop after 50 total
emitter.explode(30, 200, 400);  // burst 30 at position
emitter.explode(30);            // burst at emitter position

常见模式

随生命周期缩放、透明度与颜色
// Scale and alpha with custom easing
this.add.particles(400, 300, 'spark', {
    lifespan: 2000,
    speed: 100,
    scale: { start: 1, end: 0, ease: 'power2' },
    alpha: { start: 1, end: 0, ease: 'cubic.in' }
});
颜色插值

color 属性在粒子生命周期内沿一组颜色数组进行插值(会覆盖 tint):

this.add.particles(400, 300, 'spark', {
    lifespan: 2000, speed: 100, scale: { start: 0.5, end: 0 },
    color: [0xfacc22, 0xf89800, 0xf83600, 0x9f0404], colorEase: 'quad.out'
});
为粒子着色
this.add.particles(400, 300, 'spark', { tint: 0xff0000 });                           // static
this.add.particles(400, 300, 'spark', { tint: { start: 0xffffff, end: 0xff0000 } }); // over lifetime
重力井

GravityWell 施加平方反比重力,将粒子拉向(或使用负 power 时推离)某个点。

const emitter = this.add.particles(400, 300, 'spark', {
    speed: 100, lifespan: 4000, scale: { start: 0.4, end: 0 }, quantity: 2
});

const well = emitter.createGravityWell({
    x: 400, y: 300, power: 2, epsilon: 100, gravity: 50
});

// Update at runtime
well.x = 300;
well.power = -1;  // negative = repel

// Or create manually and add
const well2 = new Phaser.GameObjects.Particles.GravityWell(500, 200, 3, 100, 50);
emitter.addParticleProcessor(well2);
emitter.removeParticleProcessor(well2);
发射区域(随机)

RandomZone 在形状内的随机位置生成粒子。源必须具有 getRandomPoint(point) 方法——所有 Phaser 几何类(Circle、Ellipse、Rectangle、Triangle、Polygon、Line)都支持此方法,或者提供自定义源:

// Using built-in geometry
this.add.particles(400, 300, 'spark', {
    speed: 50, lifespan: 2000,
    emitZone: { type: 'random', source: new Phaser.Geom.Circle(0, 0, 100) }
});

// Custom source object (any object with getRandomPoint)
emitter.addEmitZone({
    type: 'random',
    source: {
        getRandomPoint: (point) => {
            const a = Math.random() * Math.PI * 2;
            point.x = Math.cos(a) * 100;
            point.y = Math.sin(a) * 50;
            return point;
        }
    }
});
发射区域(边缘)

EdgeZone 沿形状边缘按顺序放置粒子。源必须具有 getPoints(quantity, stepRate) 方法。Curves、Paths 以及所有几何形状都支持此方法:

this.add.particles(400, 300, 'spark', {
    lifespan: 1500, speed: 20,
    emitZone: {
        type: 'edge',
        source: new Phaser.Geom.Circle(0, 0, 150),
        quantity: 48,     // number of points on edge (use 0 with stepRate instead)
        yoyo: false,      // reverse direction at ends
        seamless: true    // remove duplicate endpoint
    }
});

// Or add post-creation with any source that has getPoints
emitter.addEmitZone({ type: 'edge', source: geom, quantity: 50, yoyo: false, seamless: true });

多个发射区域: 向 emitZone 传入数组,或多次调用 addEmitZone()。区域按顺序迭代。total 属性控制发射多少粒子后轮换到下一个区域(-1 = 永不轮换)。

this.add.particles(400, 300, 'spark', {
    emitZone: [
        { type: 'random', source: new Phaser.Geom.Circle(0, 0, 50) },
        { type: 'random', source: new Phaser.Geom.Circle(200, 0, 50) }
    ]
});
消亡区域

DeathZone 在粒子进入(或离开)某个区域时将其销毁。源必须具有 contains(x, y) 方法。

// Kill particles entering a rectangle
this.add.particles(400, 100, 'spark', {
    speed: 200, lifespan: 5000, gravityY: 100,
    deathZone: { type: 'onEnter', source: new Phaser.Geom.Rectangle(300, 400, 200, 50) }
});

// Kill particles leaving a circle (confine to area)
this.add.particles(400, 300, 'spark', {
    speed: 100, lifespan: 5000,
    deathZone: { type: 'onLeave', source: new Phaser.Geom.Circle(400, 300, 150) }
});

// Custom death zone source (any object with contains)
emitter.addDeathZone({
    type: 'onEnter',
    source: { contains: (x, y) => x > 600 && y > 400 }
});
跟随游戏对象
const player = this.add.sprite(100, 100, 'player');
const emitter = this.add.particles(0, 0, 'spark', {
    speed: 50, lifespan: 800, scale: { start: 0.5, end: 0 }
});

emitter.startFollow(player);                       // follow position
emitter.startFollow(player, 10, -20);              // with offset
emitter.startFollow(player, 0, 0, true);           // track visibility too
emitter.stopFollow();

// Or via config:
this.add.particles(0, 0, 'spark', { follow: player, followOffset: { x: 0, y: -20 } });
粒子回调
// Via config
const emitter = this.add.particles(400, 300, 'spark', {
    speed: 100, lifespan: 2000,
    emitCallback: (particle, emitter) => { /* on emit */ },
    deathCallback: (particle) => { /* on death */ }
});

// Or set after creation
emitter.onParticleEmit((particle, emitter) => { /* ... */ });
emitter.onParticleDeath((particle) => { /* ... */ });

// Iterate alive/dead particles
emitter.forEachAlive((particle, emitter) => { /* particle.x, particle.lifeT */ });
Duration、StopAfter 与 Advance
// Auto-stop after 3 seconds (alive particles continue until they expire)
this.add.particles(400, 300, 'spark', { speed: 100, duration: 3000 });

// Emit exactly 50 particles then stop
this.add.particles(400, 300, 'spark', { speed: 100, stopAfter: 50 });

// Pre-warm: fast-forward 2 seconds so particles visible on first frame
this.add.particles(400, 300, 'spark', { speed: 100, lifespan: 2000, advance: 2000 });
// Or manually: emitter.fastForward(2000, 50);
粒子边界(弹跳)
this.add.particles(400, 300, 'spark', {
    speed: 200, lifespan: 5000, bounce: 0.8,
    bounds: { x: 100, y: 100, width: 600, height: 400 },
    collideLeft: true, collideRight: true, collideTop: true, collideBottom: true
});
// Or: emitter.addParticleBounds(100, 100, 600, 400);
纹理帧与动画
// Random frame per particle
this.add.particles(400, 300, 'flares', { frame: ['red', 'green', 'blue'] });

// Sequential frames cycling through with quantity per frame
this.add.particles(400, 300, 'flares', {
    frame: { frames: ['red', 'green', 'blue'], cycle: true, quantity: 4 }
});

// Particle animation (plays anim over particle lifetime)
this.add.particles(400, 300, 'explosion', { anim: 'explode_anim', lifespan: 1000 });

// Multiple anims, randomly assigned
this.add.particles(400, 300, 'sheet', {
    anim: { anims: ['fire', 'smoke'], cycle: false, quantity: 1 }
});
对粒子排序
this.add.particles(400, 300, 'spark', { sortProperty: 'y', sortOrderAsc: true });
// Or: sortCallback: (a, b) => a.y - b.y
自定义粒子处理器

继承 ParticleProcessor,为每帧应用自定义的逐粒子逻辑。实现 update(particle, delta, step, t):

class WindProcessor extends Phaser.GameObjects.Particles.ParticleProcessor {
    constructor (windX, windY) {
        super(0, 0);
        this.windX = windX;
        this.windY = windY;
    }

    update (particle, delta, step, t) {
        particle.velocityX += this.windX * step;
        particle.velocityY += this.windY * step;
    }
}

emitter.addParticleProcessor(new WindProcessor(0.5, 0));
自定义粒子类

继承 Particle 并重写 update 以实现逐粒子行为。在配置中通过 particleClass 设置:

class TrailParticle extends Phaser.GameObjects.Particles.Particle {
    update (delta, step, processors) {
        const result = super.update(delta, step, processors);
        this.alpha = this.lifeT;  // custom: alpha matches life progress
        return result;  // must return true if particle is still alive
    }
}

this.add.particles(400, 300, 'spark', {
    particleClass: TrailParticle,
    speed: 100, lifespan: 2000
});

配置参考

ParticleEmitterConfig -- 简单属性
属性类型默认值描述
activebooleantrueFalse = 发射器完全不更新
emittingbooleantrueFalse = 不产生新粒子(存活粒子仍会更新)
blendModestring/number0用于渲染的混合模式
frequencynumber0流循环之间的毫秒数;0 = 每帧;-1 = 爆炸
gravityX, gravityYnumber0重力,单位为 px/s^2
maxParticlesnumber0粒子对象总数的硬性上限(0 = 无限制)
maxAliveParticlesnumber0同时存活的粒子数上限(0 = 无限制)
durationnumber0在指定毫秒数后自动停止(0 = 永久)
stopAfternumber0发射 N 个粒子后自动停止(0 = 无限制)
advancenumber0创建时快进(毫秒)
radialbooleantrueTrue = speed+angle;false = speedX/speedY
particleBringToTopbooleantrue新粒子渲染在最上层
timeScalenumber1更新时的时间倍率
followVector2Likenull要跟随的对象
followOffsetVector2Like相对于跟随目标的偏移
trackVisiblebooleanfalse匹配跟随目标的可见性
reservenumber预分配粒子对象
particleClassfunctionParticle自定义粒子类
sortPropertystring用于排序的粒子属性
sortOrderAscboolean若为 true 则按升序排序
ParticleEmitterConfig -- EmitterOp 属性

全部接受上文所述的灵活取值格式。

属性默认值E/U描述
x, y0E+U粒子相对于发射器的偏移
speed0E径向速度(设置 speedX,停用 speedY)
speedX, speedY0E定向速度(设置 radial=false)
angle{min:0,max:360}E发射角度,单位为度
scale1E+U统一缩放(设置 scaleX,停用 scaleY)
scaleX, scaleY1E+U非统一缩放
alpha1E+UAlpha 透明度
rotate0E+U旋转角度,单位为度
tint0xffffffE+U着色颜色(WebGL)
colorE+U用于插值的颜色数组(覆盖 tint)
colorEase颜色插值的缓动
lifespan1000E生命周期,单位为毫秒
delay0E可见前的延迟(毫秒)
hold0E在生命结束前保持的时间(毫秒)
quantity1E每个流循环的粒子数
accelerationX/Y0E+U加速度(px/s^2)
maxVelocityX/Y10000E+U最大速度
bounce0E+U反弹恢复系数(0-1)
moveToX, moveToY0E+U目标位置(覆盖 angle/speed)

E = 仅发射,E+U = 发射 + 更新(支持 start/end、onUpdate)

区域配置属性
配置键类型属性
emitZoneobject 或 array{ type: 'random', source: <shape> }
{ type: 'edge', source: <shape>, quantity, stepRate, yoyo, seamless, total }
deathZoneobject 或 array{ type: 'onEnter'|'onLeave', source: <shape> }
boundsobject{ x, y, width, height } 或 { x, y, w, h }

事件

所有事件都在 ParticleEmitter 实例本身上发出。

事件字符串回调参数触发时机
START'start'(emitter)调用 start() 且发射器开始发射时
STOP'stop'(emitter)调用 stop(),或达到 duration/stopAfter 限制时
COMPLETE'complete'(emitter)发射器停止后最后一个存活粒子消亡时
EXPLODE'explode'(emitter, particle)调用 explode() 时
DEATH_ZONE'deathzone'(emitter, particle, zone)死亡区域杀死一个粒子时
emitter.on('stop', (emitter) => { /* stopped emitting */ });
emitter.on('complete', (emitter) => { /* all particles dead */ });
emitter.on('deathzone', (emitter, particle, zone) => { /* ... */ });

API 快速参考

ParticleEmitter 关键方法

生命周期: start(advance?, duration?)、stop(kill?)、pause()、resume()、flow(frequency, count?, stopAfter?)、explode(count?, x?, y?)、emitParticleAt(x?, y?, count?)、emitParticle(count?, x?, y?)、fastForward(time, delta?)。

配置: setConfig(config)、updateConfig(config)。

跟随: startFollow(target, offX?, offY?, trackVisible?)、stopFollow()。

区域: addEmitZone(config)、removeEmitZone(zone)、clearEmitZones()、addDeathZone(config)、removeDeathZone(zone)、clearDeathZones()。

处理器: createGravityWell(config)、addParticleProcessor(processor)、removeParticleProcessor(processor)、getProcessors()。

边界: addParticleBounds(x, y, w, h, collideL?, collideR?, collideT?, collideB?)。

回调/迭代: onParticleEmit(cb, ctx?)、onParticleDeath(cb, ctx?)、killAll()、forEachAlive(cb, ctx?)、forEachDead(cb, ctx?)。

计数: getAliveParticleCount()、getDeadParticleCount()、getParticleCount()、atLimit()、reserve(count)。

属性设置器: setParticleSpeed(x, y?)、setParticleScale(x, y?)、setParticleGravity(x, y)、setParticleAlpha(value)、setParticleTint(value)、setParticleLifespan(value)、setEmitterAngle(value)、setQuantity(qty)、setFrequency(freq, qty?)、setRadial(value)、setEmitterFrame(frames, random?, qty?)、setAnim(anims, random?, qty?)。

排序: setSortProperty(property, ascending?)、setSortCallback(callback)、depthSort()。

实用工具: getBounds(padding?, advance?, delta?, output?)、overlap(target)。

GravityWell
属性/方法描述
x, y引力井的世界坐标位置
power力的大小(负值表示排斥)
epsilon力计算的最小距离(默认 100)
gravity引力常数(默认 50)
active启用/禁用处理(继承自 ParticleProcessor)

构造函数: new GravityWell(x, y, power, epsilon, gravity) 或 new GravityWell(config),其中 config 为 { x, y, power, epsilon, gravity }。

注意事项

  • 没有 ParticleEmitterManager: 在 v3.60 中已移除。this.add.particles() 直接返回一个 ParticleEmitter。
  • speed 与 speedX/speedY: speed 会设置 speedX 并停用 speedY(径向)。speedX/speedY 会切换到点模式(radial: false)。
  • scale 与 scaleX/scaleY: scale 会应用于 scaleX 并停用 scaleY。若要实现非等比缩放,请同时使用两者。
  • color 会覆盖 tint: 两者互斥;color(数组)优先。
  • moveToX/moveToY: 必须同时设置才会生效。会覆盖 angle 和 speed。
  • emitting 与 active: emitting: false = 不产生新粒子,但存活粒子仍会更新。active: false = 整个发射器冻结。
  • stop 与 complete: 当发射停止时会触发 'stop'。当最后一个存活粒子死亡时会触发 'complete'。
  • frequency: 0: 表示每帧都发射(最大速率),而不是“从不”。若要阻止发射,请使用 emitting: false。
  • frequency: -1: 会让发射器进入爆炸模式——它不会自动持续发射。使用 explode() 来发射爆发。
  • hold 会冻结粒子: 在生命周期结束后,hold 会让粒子保持可见并冻结指定的毫秒数,然后才消亡。适用于拖尾/滞留效果。
  • advance 会快进: 通过在创建时模拟给定的毫秒数来预预热发射器,因此粒子在第一帧就已经可见。
  • reserve(count) 会预分配: 调用 reserve() 或在配置中设置 reserve,以预先创建粒子对象,避免游戏过程中按需分配导致的 GC 峰值。
  • 区域源方法: RandomZone 需要 getRandomPoint(point)。EdgeZone 需要 getPoints(quantity, stepRate)。DeathZone 需要 contains(x, y)。
  • 粒子池: maxParticles 限制对象总数(不是存活数量)。可见数量限制请使用 maxAliveParticles。
  • 需要纹理: 发射器需要一个有效的纹理键。多帧纹理请使用 frame 配置。

源文件

完整源文件映射请参见 references/REFERENCE.md。关键入口点:src/gameobjects/particles/ParticleEmitter.js(主类)、src/gameobjects/particles/Particle.js(单个粒子)、src/gameobjects/particles/zones/(区域类)。

评论

支持 Markdown