particles について
Phaser 4 開発者向けのパーティクルシステムスキルです。ParticleEmitter の作成と設定から、発生、更新、寿命の制御まで扱い、発生モードやゾーン、独自の処理方法も解説します。
主な機能
- 01ParticleEmitter の作成と設定
- 02フロー発生とバースト発生
- 03寿命プロパティと色補間
- 04重力井戸と発生・消滅ゾーン
- 05ゲームオブジェクト追従とコールバック
- 06独自パーティクルクラスとプロセッサー
活用シーン
- 01連続噴射や一度限りのバーストの作成
- 02ジオメトリ内でのパーティクル生成や消去
- 03ゲームオブジェクトに追従する効果の作成
- 04フレームごとの独自パーティクル処理の実装
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ phaserjs/ phaser --skill particlesインストール後、Claude Code で/particlesを入力して読み込まれたか確認します。
同梱ファイルreferences/REFERENCE.md
詳細
パーティクルシステム
Phaser 4 でパーティクルエフェクトを作成および制御する -- ParticleEmitter の作成と設定、エミッター操作(値の形式)、重力ウェル、放出ゾーンと消滅ゾーン、フローモードとバーストモード、ゲームオブジェクトへの追従、およびパーティクルコールバック。
主要なソースパス: 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 を拡張し、ディスプレイリストに直接追加されます。これはゲームオブジェクト(位置指定、拡大縮小、マスク可能)であると同時に、エミッターそのものです。独立したマネージャーは存在しません -- 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)
ミックスイン: AlphaSingle、BlendMode、Depth、Lighting、Mask、RenderNodes、ScrollFactor、Texture、Transform、Visible。したがって、setPosition()、setScale()、setDepth()、setBlendMode()、setMask()、setScrollFactor() などを呼び出せます。
Particle
エミッターが所有する軽量オブジェクトです。主なプロパティ: 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。
フローとエクスプロード(バースト)
フローモード(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) メソッドが必要です。Curve、Path、およびすべてのジオメトリ図形がこれをサポートしています:
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 -- シンプルプロパティ
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
active | boolean | true | false = エミッターはまったく更新されません |
emitting | boolean | true | false = 新しいパーティクルを生成しません(生存中のパーティクルは引き続き更新されます) |
blendMode | string/number | 0 | レンダリング時のブレンドモード |
frequency | number | 0 | フローサイクル間のミリ秒。0 = 毎フレーム、-1 = 爆発 |
gravityX, gravityY | number | 0 | 重力(px/s^2) |
maxParticles | number | 0 | パーティクルオブジェクトの総数の上限(0 = 無制限) |
maxAliveParticles | number | 0 | 同時に生存できるパーティクルの最大数(0 = 無制限) |
duration | number | 0 | 指定ミリ秒後に自動停止(0 = 無期限) |
stopAfter | number | 0 | N 個のパーティクルを放出した後に自動停止(0 = 無制限) |
advance | number | 0 | 生成時に早送りする時間(ミリ秒) |
radial | boolean | true | true = speed+angle、false = speedX/speedY |
particleBringToTop | boolean | true | 新しいパーティクルを最前面に描画します |
timeScale | number | 1 | 更新時の時間倍率 |
follow | Vector2Like | null | 追従するオブジェクト |
followOffset | Vector2Like | 追従ターゲットからのオフセット | |
trackVisible | boolean | false | 追従ターゲットの表示状態に合わせます |
reserve | number | パーティクルオブジェクトを事前に確保します | |
particleClass | function | Particle | カスタムパーティクルクラス |
sortProperty | string | ソートに使用するパーティクルのプロパティ | |
sortOrderAsc | boolean | true の場合は昇順でソートします |
ParticleEmitterConfig -- EmitterOp プロパティ
すべて、上記で説明した柔軟な値の形式を受け付けます。
| プロパティ | デフォルト | E/U | 説明 |
|---|---|---|---|
x, y | 0 | E+U | エミッターからのパーティクルのオフセット |
speed | 0 | E | 放射方向の速度(speedX を設定し、speedY を無効化します) |
speedX, speedY | 0 | E | 方向別の速度(radial=false を設定します) |
angle | {min:0,max:360} | E | 放出角度(度) |
scale | 1 | E+U | 均一スケール(scaleX を設定し、scaleY を無効化します) |
scaleX, scaleY | 1 | E+U | 非均一スケール |
alpha | 1 | E+U | アルファ透明度 |
rotate | 0 | E+U | 回転(度) |
tint | 0xffffff | E+U | ティントカラー(WebGL) |
color | E+U | 補間するカラー配列(tint を上書きします) | |
colorEase | カラー補間のイージング | ||
lifespan | 1000 | E | 生存時間(ミリ秒) |
delay | 0 | E | 表示されるまでの遅延(ミリ秒) |
hold | 0 | E | 消滅前に寿命の終わりで保持する時間(ミリ秒) |
quantity | 1 | E | フローサイクルごとのパーティクル数 |
accelerationX/Y | 0 | E+U | 加速度(px/s^2) |
maxVelocityX/Y | 10000 | E+U | 最大速度 |
bounce | 0 | E+U | 反発係数(0~1) |
moveToX, moveToY | 0 | E+U | ターゲット位置(angle/speed を上書きします) |
E = 放出のみ、E+U = 放出 + 更新(start/end、onUpdate をサポート)
Zone 設定プロパティ
| 設定キー | 型 | プロパティ |
|---|---|---|
emitZone | object または array | { type: 'random', source: <shape> } |
{ type: 'edge', source: <shape>, quantity, stepRate, yoyo, seamless, total } | ||
deathZone | object または array | { type: 'onEnter'|'onLeave', source: <shape> } |
bounds | object | { 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/(ゾーンクラス)。