mcpField
P
phaser-particles

phaser-particles

github.com/yakoub-ai開発2969 回インストール9 週間前に更新
リポジトリ

phaser-particles について

phaser-particlesは、Phaser 4のゲーム開発者向けにパーティクル効果の構築と調整を案内するスキルです。エミッターや効果の設定に加え、粒子数の制限や実行時の検証も扱います。

主な機能

  1. 01バーストと連続エミッター
  2. 02粒子の動きと外観の設定
  3. 03発生ゾーンと消滅ゾーン
  4. 04移動するゲームオブジェクトへの追従
  5. 05粒子数とパフォーマンスの指針
  6. 06実行時のパーティクル検証

活用シーン

  1. 01ヒット時の火花や取得時の演出を追加
  2. 02雨、煙、炎の効果を作成
  3. 03画面外の粒子による負荷を抑制
  4. 04高負荷時の粒子数をテスト

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/yakoub-ai/phaser4-gamedev --skill phaser-particles

インストール後、Claude Code で/phaser-particlesを入力して読み込まれたか確認します。

同梱ファイルreferences/particle-recipes.md

詳細

Phaser 4 パーティクル

パーティクルは、ゲームが反応しているように感じられるかどうかを左右します。ヒット時に 3 つの火花を出すだけで、同じヒットでも 2 倍しっかりした感触になり、他のあらゆる「ジュース」演出よりもはるかに少ない手間で済みます。

同時に、Phaser のゲームが 20fps で動いてしまう最も一般的な原因でもあります。


エミッター

const emitter = this.add.particles(x, y, 'texture', config);

1 回の呼び出しでエミッターを作成して追加します。これは通常のゲームオブジェクトです。depth、scroll factor、alpha を持ち、コンテナーに入れることもできます。

実用上重要なフィールドをすべて含めた設定例:

const sparks = this.add.particles(0, 0, 'flare', {
  // Motion
  speed: { min: 50, max: 150 },      // px/sec; a range gives natural spread
  angle: { min: 0, max: 360 },       // degrees; direction of travel
  gravityY: 300,                     // px/sec² — falling embers, debris
  accelerationX: 0,

  // Appearance over life — {start, end} interpolates across the particle's lifespan
  scale: { start: 1, end: 0 },
  alpha: { start: 1, end: 0 },
  rotate: { start: 0, end: 360 },
  tint: [0xffaa00, 0xff2200],        // an array picks randomly per particle
  blendMode: 'ADD',                  // ADD for light: fire, magic, sparks

  // Timing
  lifespan: 600,                     // ms each particle lives
  frequency: 40,                     // ms between emissions; -1 = explode mode only
  quantity: 2,                       // particles per emission

  // Budget — always set this
  maxParticles: 100,

  emitting: false,                   // create dormant; start it deliberately
});

{ start, end } が主力です。 パーティクルエフェクトが「撒き散らした」ではなく「意図的に見える」かどうかの大部分は、ライフスパンにわたって scale と alpha が縮小・フェードすることにかかっています。最大サイズのまま消えるパーティクルはバグのように見えます。


1 回だけ放出するか、連続して放出するか

2 つのモードがあり、これを混同すると、エフェクトがまったく現れないか、永遠に止まらないかのどちらかになります。

バースト — 爆発、ヒットの火花、アイテム取得のポップ:

const burst = this.add.particles(0, 0, 'spark', {
  speed: { min: 100, max: 300 },
  lifespan: 400,
  scale: { start: 0.8, end: 0 },
  emitting: false,                    // dormant until told otherwise
});

burst.explode(20, x, y);              // 20 particles, right here, right now

連続 — たいまつ、煙の柱、雨:

const smoke = this.add.particles(x, y, 'smoke', {
  frequency: 100,
  quantity: 1,
  lifespan: 2000,
  emitting: true,
});

smoke.stop();      // stop emitting; existing particles live out their lifespan
smoke.start();
smoke.pause();     // freeze everything, including live particles
smoke.resume();

stop() と pause() は異なり、その違いは目に見えます。stop() は煙の柱を自然に消散させ、pause() は空中で凍結させます。

クリーンアップ: 爆発ごとにエミッターを作成して破棄しないのはリークです。エミッターを 1 つ作成して再 explode() するか、最後のパーティクルが消えた後に破棄してください:

// Reuse — the right default
this.hitSparks.explode(8, enemy.x, enemy.y);

// Or fire-and-forget, cleaned up properly
const e = this.add.particles(x, y, 'spark', { lifespan: 400, emitting: false });
e.explode(20);
this.time.delayedCall(500, () => e.destroy());

移動するオブジェクトを追従する

emitter.startFollow(player, 0, -10);   // offsetX, offsetY
emitter.stopFollow();

update() でエミッターの位置を設定するのではなく、これを使ってください。startFollow はフレーム間を追跡するため、高速で移動するオブジェクトは、各フレームの位置に塊が点線状に並ぶのではなく、連続した軌跡を残します。


ゾーン

放出ゾーン — パーティクルが生まれる場所:

// Along the edge of a shape: a ring, a magic circle
emitter.addEmitZone({
  type: 'edge',
  source: new Phaser.Geom.Circle(0, 0, 50),
  quantity: 32,
});

// Anywhere inside a shape: rain across the top of the screen
emitter.addEmitZone({
  type: 'random',
  source: new Phaser.Geom.Rectangle(0, 0, 800, 10),
});

消滅ゾーン — パーティクルが破棄される場所:

emitter.addDeathZone({
  type: 'onEnter',                                  // or 'onLeave'
  source: new Phaser.Geom.Rectangle(0, 400, 800, 200),
});

画面サイズの矩形と組み合わせた onLeave は、画面外のパーティクルにコストをかけないようにする方法です。

重力井戸 — 引き寄せるか反発させるか:

emitter.createGravityWell({ x: 400, y: 300, power: 3, epsilon: 100, gravity: 100 });

渦、ブラックホール、コインを引き寄せる磁石のアイテムなどです。


パフォーマンス — リリース前にこれを読んでください

パーティクルは Phaser のゲームでフレームレートが崩壊する最も一般的な原因であり、その崩壊はコードを書いてからずっと後に訪れます。負荷がかかったときにだけ現れるからです。

必ず maxParticles を設定してください。 上限がなく frequency が短いエミッターは、フレームバジェットがなくなるまで平気で確保し続けます。これはあらゆるエミッター設定の中で、単独で最も価値の高い一行です。

maxParticles: 100,       // hard ceiling

サイズの見積もり: lifespan / frequency * quantity が定常状態の数です。lifespan: 2000, frequency: 40, quantity: 2 では、1 つのエミッターから 100 個のパーティクルが生きていることになります。そして、そのようなエミッターが 10 個あれば 1000 個です。

その他のレバー、おおよそ効果の大きい順:

レバー理由
lifespan を短くする同じ視覚的密度でも生きているパーティクルが減る
onLeave での消滅ゾーン画面外のパーティクルは見えているものとまったく同じコストがかかる
より少なく、より大きく大きな火花 20 個は小さなもの 200 個より見栄えがよく、コストは 10 分の 1
エミッターを 1 つ使い回すエミッターの生成と破棄はパーティクル本体よりコストがかかる
テクスチャを小さくするパーティクルのテクスチャが 32×32 を超える必要はほとんどない
モバイルでは ADD を避ける加算ブレンドはフィルレートを消費し、モバイルはフィルレートに制約される
stopAfterN 個のパーティクルを放出して自動的に停止 — 忘れがちなクリーンアップロジックが不要

モバイルのバジェット: エフェクトごとではなく、ゲーム全体で数百個の同時パーティクルです。デスクトップでの数を半分にして、実機で確認してください — skills/phaser-mobile/references/device-profiles.md を参照してください。

数千個の類似したスプライトには、パーティクルは間違ったツールです — skills/phaser-fx/references/renderer-and-shaders.md の SpriteGPULayer を参照してください。


パーティクルが表示されない理由

おおよそ可能性の高い順に並べています。

症状原因
まったく何も出ないemitting: false かつ start() や explode() を一度も呼んでいない
まったく何も出ないテクスチャキーが存在しない — ローダーと、プレイテスト時のアセットチェックを確認してください
まったく何も出ないfrequency: -1(explode 専用)なのに explode() を呼んでいない
見えないエミッターの深度が背景より下にある — emitter.setDepth(n)
見えないalpha: { start: 0 } — 終了値ではなく開始値が 0 になっている
1 フレームだけ表示されて消えるlifespan が短すぎる
位置がおかしいエミッターの x/y はワールド座標です。コンテナ内ではローカル座標になります
追従しない位置を startFollow() ではなく update() で設定している
フレームレートが崩壊するmaxParticles がない

実行時にパーティクルを検証する

パーティクルのバグは型チェックを完璧に通過します — 誤ったテクスチャキーは文字列ですし、一度も起動しないエミッターも有効なコードです。実際に動作したことを証明しましょう。

// playtest/particles-check.mjs
export default [
  { name: 'hit sparks fire on damage', action: 'eval',
    code: `scene('GameScene').hitEnemy(scene('GameScene').enemies[0])` },
  { name: 'particles actually spawned', action: 'expect',
    expect: { expression: `scene('GameScene').hitSparks.getAliveParticleCount()`, atLeast: 1 } },

  { name: 'and they are capped', action: 'sample',
    expression: `scene('GameScene').hitSparks.getAliveParticleCount()`,
    duration: 3000, interval: 100,
    expect: { stat: 'max', atMost: 100 } },

  { name: 'frame rate survives the effect', action: 'sample',
    expression: `game.loop.actualFps`, duration: 3000, interval: 100,
    expect: { stat: 'min', atLeast: 50 } },
];

生存数の max アサーションこそ、上限のないエミッターをプレイヤーより先に捕まえるものです。ゲームが最も重いパーティクル負荷にあるときに実行してください。アイドル状態でではありません。


その他のリソース

リファレンスファイル
  • references/particle-recipes.md — ゲームで実際に必要になるエフェクトの完全な設定: 爆発、炎、煙、雨、雪、火花、血、魔法、塵、マズルフラッシュ、紙吹雪、軌跡 — それぞれの数値の根拠付きです。
関連
  • skills/phaser-fx/ — フィルターとブレンドモード。ADD パーティクルとブルームアクションの組み合わせが、標準的な「魔法」の見た目です
  • skills/phaser-analyze/references/performance-playbook.md — パーティクルがフレームを消費する場合
  • skills/phaser-mobile/references/device-profiles.md — モバイルでのパーティクル予算

コメント