particle-system について
Decentraland SDK7 の ParticleSystem エフェクトを設定するためのスキルです。エミッターの構成、粒子の動作、エディターでの調整、実用上の上限を、シーン制作者向けに説明します。
主な機能
- 01Point、Sphere、Cone、Box のエミッター
- 02連続発生とバースト発生
- 03色、サイズ、動き、スプライトシートの制御
- 04再生状態とシミュレーション空間の設定
- 05Creator Hub インスペクターの利用案内
活用シーン
- 01ループまたは単発のシーンエフェクト作成
- 02軌跡、降雪、火花、花火の制作
- 03Creator Hub での粒子エミッター調整
- 04シーンの予算内に粒子数を維持
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ decentraland/ sdk-skills --skill particle-systemインストール後、Claude Code で/particle-systemを入力して読み込まれたか確認します。
同梱ファイルreferences/particle-presets.md
詳細
ParticleSystem(SDK7)
エンティティからパーティクルを放出します。1つのエンティティにつきParticleSystemコンポーネントは1つで、Transformと一緒にアタッチします。メッシュは不要です。パーティクルはコンポーネント自体から描画されます。
ルール:Transform.scaleはパーティクルのサイズに影響しません
パーティクルのサイズは、initialSizeとsizeOverTime(FloatRange)のみで制御されます。また、ParticleSystemは、サイズフィールドを持つ場合にエミッター形状の空間寸法(Sphereのradius、Boxのsize、Coneのradius)も設定します。これらはエンティティのTransform.scaleの影響を受けません。Transform.scaleを大きくしても、パーティクルは大きくなりません。
ルール:パーティクルが描画されるのは、シーンの区画内にいるプレイヤーだけです
区画の外からシーンを見ているプレイヤーには、何も表示されません。パーティクルはシーンのLODシルエットには含まれません。エミッターは区画の境界内に配置してください。
ルール:エンジンはパーティクルの合計数を約1000個に制限します
エンジンはシーンごとのパーティクル予算を設けており、生存中のパーティクルの合計数が上限を超える場合、アクティブなすべてのパーティクルシステムの放出率を引き下げます。各システムのmaxParticlesを設定し、小規模なシステムを多数使うよりも、効果の高いシステムを少数使うようにしてください。
モバイルの予算(公開されている数値)
モバイルクライアントでは、具体的な数値が公開されています(ドキュメントのコミット8139a7f、2026年10月):
| ソフト上限 | ハード上限 | |
|---|---|---|
| VFXエミッター(シーン内のパーティクルシステム) | 8 | 10 |
| 生存中のパーティクル | 25,000 | 50,000 |
ソフト上限はモバイルのパフォーマンスパネルに警告が表示される値、ハード上限はシーンが読み込まれなくなる値です。これらのシーン上限に加え、モバイルでは実行時にエミッターあたり5,000個、シーンあたり50,000個に制限されます。この上限を超えて設定しても、パーティクル数が通知なく削減されるため、maxParticlesを5,000より大きくしても予算を無駄にするだけで、表示上の効果はありません。
実用上の目標は、エミッターを8個以下にし、各エミッターのmaxParticlesを5,000より十分小さく設定して、同時に生存するパーティクルの合計数を控えめに保つことです。[未検証:上記のデスクトップ版「約1000」という数値が、これらのモバイル版の数値とどのように関係するかは不明です。異なる情報源に基づくため、クロスプラットフォームのシーンでは、より厳しい方を設計目標として扱ってください。]
モバイルの上限一覧とライト/パーティクルの根拠については、optimize-scene → モバイルシーンの上限を参照してください。ParticleSystem自体はモバイルでサポートされています(もはや機能差はありません)。
ルール:prewarmにはloop = trueが必要です
prewarm: trueが有効になるのは、loop: trueの場合のみです。ワンショットシステム(loop: false)では、prewarmは通知なく無視されます。
ルール:faceTravelDirectionはbillboardより優先されます
faceTravelDirection: trueの場合、パーティクルは速度ベクトルに沿って向きを変え、billboardは無視されます。トレイルや筋状のエフェクト(小惑星、弾丸、火花)にはこちらを使用してください。混乱を避けるため、billboard: falseを明示的に設定してください。
インポート
import { engine, Transform, ParticleSystem } from '@dcl/sdk/ecs'
import {
PBParticleSystem_BlendMode,
PBParticleSystem_PlaybackState,
PBParticleSystem_SimulationSpace,
} from '@dcl/sdk/ecs'
import { Color4, Vector3, Quaternion } from '@dcl/sdk/math'
ParticleSystemBlendModeとParticleSystemPlaybackStateのエイリアスも@dcl/sdk/ecsからエクスポートされており、PBプレフィックス付きの名前と同じように使用できます。ParticleSystemSimulationSpaceエイリアスはありません。PBParticleSystem_SimulationSpaceのみです。一貫性を保つため、どこでもPBプレフィックス付きの名前を使用してください。
フィールドリファレンス
| フィールド | 型 | デフォルト | 備考 |
|---|---|---|---|
active | boolean | true | 新規の放出を一括で有効/無効にします。 |
rate | number | 10 | 連続して毎秒放出されるパーティクル数です。bursts を使用する場合は 0 に設定します。 |
maxParticles | number | 1000 | このシステムで同時に生存できるパーティクル数の上限です。 |
lifetime | number | 5 | パーティクルの寿命(秒)です。 |
gravity | number | 0 | シーンの重力(約 -9.81 m/s²)に対する倍率です。負の値にするとパーティクルは上昇します。 |
additionalForce | Vector3 | — | 毎フレーム適用される一定の力のベクトルです(ワールド空間)。 |
initialSize | FloatRange | {start:1, end:1} | 生成時のランダムなサイズです。 |
sizeOverTime | FloatRange | {start:1, end:1} | パーティクルの寿命にわたって開始値から終了値へ補間されるサイズです。 |
initialRotation | Quaternion | identity | 生成時の向き(3D)です。 |
rotationOverTime | Quaternion | identity | 軸ごとの角速度です(クォータニオンをオイラー XYZ の rad/s 相当値に変換)。 |
faceTravelDirection | boolean | false | 速度の方向に向けます。billboard より優先されます。 |
initialColor | ColorRange | {white,white} | 生成時のランダムな色です。 |
colorOverTime | ColorRange | {white,white} | 寿命にわたって開始色から終了色へ補間される色です。終了時にフェードアウトさせるには、alpha=0 を使用します。 |
initialVelocitySpeed | FloatRange | {start:1, end:1} | 初速(m/s)です。パーティクルごとにランダム化されます。 |
texture | Texture | 白い四角形 | パーティクルのスプライトです。Material のテクスチャと同じ Texture 形式です。 |
blendMode | PBParticleSystem_BlendMode | PSB_ALPHA | PSB_ALPHA | PSB_ADD | PSB_MULTIPLY。 |
billboard | boolean | true | パーティクルを常にカメラの方向に向けます。3D 方向に向けるパーティクルでは無効にします。 |
spriteSheet | {tilesX, tilesY, framesPerSecond?} | — | テクスチャアトラスのフレームアニメーションです。framesPerSecond のデフォルト値は 30 です。 |
shape | oneof Point/Sphere/Cone/Box | Point | エミッターの形状です。以下を参照してください。 |
loop | boolean | true | 放出サイクルをループします。false の場合は 1 回のみです。 |
prewarm | boolean | false | 1 ループ分のシミュレーションがすでに完了した状態で開始します。loop: true が必要です。 |
simulationSpace | PBParticleSystem_SimulationSpace | PSS_LOCAL | PSS_LOCAL(エンティティとともに移動)| PSS_WORLD(生成後はその場に留まる)。 |
limitVelocity | {speed, dampen?} | — | 最高速度を制限します。dampen は 0~1 で、デフォルトの 1 は強制的な上限設定です。 |
playbackState | PBParticleSystem_PlaybackState | PS_PLAYING | PS_PLAYING | PS_PAUSED | PS_STOPPED。 |
bursts | {values: Burst[]} | — | 個別の放出イベントです(「バースト」を参照)。 |
FloatRange = { start: number, end: number } — 粒子ごとにサンプリングされるランダムな値です。 | |||
ColorRange = { start: Color4, end: Color4 } — 粒子ごとにサンプリングされるランダムな色です(範囲の各端点は、ほかの粒子のランダム値とは独立してサンプリングされます)。 |
エミッターの形状
ParticleSystem.Shape.* ヘルパーを使って作成してください。oneof を手動で組み立てないでください。
shape: ParticleSystem.Shape.Point()
shape: ParticleSystem.Shape.Sphere({ radius: 1 }) // default radius=1
shape: ParticleSystem.Shape.Cone({ angle: 25, radius: 1 }) // angle = half-angle in degrees
shape: ParticleSystem.Shape.Box({ size: Vector3.create(1, 1, 1) })
- Point — エンティティの原点から放出します。
- Sphere —
radiusで指定した球の内部からランダムな点を選びます。 - Cone — 底面の円盤から放出し、半角
angleの範囲で外側に投影します。デフォルトの円錐の向きはエンティティのローカル前方です。向きを変えるには親の Transform を回転させてください。 - Box —
sizeで指定した軸に平行な直方体の内部からランダムな点を選びます。
放出方向を回転させる(雪や雨のために円錐を下向きにする場合など)には、親エンティティの Transform.rotation を回転させてください。例:円錐から雪を降らせるには、エンティティに rotation: Quaternion.fromEulerDegrees(180, 0, 0) を設定するか、円錐の軸を反転させる任意の向きにします。
バースト
特定の時刻に発生する離散的な放出イベントです。バーストのみを使う場合は rate: 0 に設定し、連続放出とバーストを組み合わせる場合は rate > 0 に設定します。
bursts: {
values: [{ time: 0, count: 100, cycles: 1, interval: 0.01, probability: 1.0 }]
}
バーストのフィールド:time(サイクル開始からの秒数)、count(バーストあたりの粒子数)、cycles(デフォルトは 1、0 は無限)、interval(サイクル間の秒数、デフォルトは 0.01)、probability(サイクルごとの発生確率、0~1、デフォルトは 1)。
1 サイクル内で複数のバーストを設定すると、点火が時間差で起こるパターンになります(花火)。
よく使われるパターン
ParticleSystem 定義の構成例です。Transform も持つエンティティにアタッチしてください。
const emitter = engine.addEntity()
Transform.create(emitter, { position: Vector3.create(8, 1, 8) })
ParticleSystem.create(emitter, {
rate: 20, // particles per second
lifetime: 2, // seconds each particle lives
maxParticles: 100,
initialSize: { start: 0.1, end: 0.3 }, // FloatRange sampled per particle
sizeOverTime: { start: 1.0, end: 0.0 }, // shrink to nothing over lifetime
initialVelocitySpeed: { start: 1, end: 2 },
colorOverTime: {
// fade out via alpha = 0 at end
start: Color4.create(1, 1, 1, 1),
end: Color4.create(1, 1, 1, 0),
},
shape: ParticleSystem.Shape.Point(), // emitter geometry
})
本番環境ですぐに使える完全な設定については、{baseDir}/references/particle-presets.md を参照してください。17 種類のプリセットがあります:Fire Ember、Magic Aura、Snowfall、Vortex Spiral、Gravity Fountain、Bat Swarm、Tumbling Leaves、Lightning Sparks、Heavy Rain、One-Shot Burst、Asteroid Trail、Purple Swirl、Bee Swarm、Fireworks Loop、Campfire、Flame Wisps、Moving Trail。爆発やピックアップの VFX(Point/Cone + ADD fire、単発の Sphere バースト)もすべてここにあります。例:Fire Ember(プリセット 1)、One-Shot Burst による爆発(プリセット 10)、ループする Fireworks Loop(プリセット 14)。
Creator Hub エディターとの統合
Creator Hub のインスペクターには専用の ParticleSystem パネルがあります(確認済みのソース:ParticleSystemInspector.tsx、複数エンティティに対応し、EntityInspector.tsx に登録済み)。Creator Hub のシーンでパーティクルエフェクトを作成・調整するには、次の方法を推奨します。
- エディターでエンティティに
ParticleSystemコンポーネントを追加します。 - インスペクターパネルですべてのフィールドをリアルタイムに調整します。コードもシーンの再読み込みも不要です。
コード(src/ 内の ParticleSystem.create/getMutable、または Script コンポーネント経由)の使用は、エミッターの切り替えをゲームロジックから行う、再生/停止をトリガーやアクションに接続する、実行時に設定を計算するなど、実行時の動的な動作に限ってください。
プログラムによる操作/複合コンポーネント名
- コンポーネント名:
core::ParticleSystem(コンポーネント ID1217)。 shapeは protobuf の oneof で、{"$case":"sphere","sphere":{"radius":1}}、{"$case":"cone","cone":{"angle":25,"radius":1}}、{"$case":"box","box":{"size":{"x":1,"y":1,"z":1}}}、または{"$case":"point","point":{}}としてシリアライズされます。textureはフラットなTextureです(TextureUnionではありません)。{"src":"assets/Images/spark.png"}としてシリアライズされます。Materialで使われる$case/texのラッパーはここでは適用されません。
再生制御
const ps = ParticleSystem.getMutable(entity)
// Pause emission and freeze existing particles
ps.playbackState = PBParticleSystem_PlaybackState.PS_PAUSED
// Resume
ps.playbackState = PBParticleSystem_PlaybackState.PS_PLAYING
// Stop and clear all live particles
ps.playbackState = PBParticleSystem_PlaybackState.PS_STOPPED
// Disable new emission (existing particles continue to live out their lifetime)
ps.active = false
即時に停止するには playbackState = PS_STOPPED を使います。自然に徐々に消えていくようにするには active = false を使います。
スプライトシートアニメーション
フレームをグリッド状に配置したテクスチャアトラスです(左から右、上から下の順)。総フレーム数は tilesX * tilesY です。
texture: { src: 'assets/Images/flame-sheet.png' },
spriteSheet: { tilesX: 4, tilesY: 3, framesPerSecond: 12 } // 12 frames, plays at 12fps
テストシーンの例では、4x4(16 フレーム、24fps のコウモリの群れ)、4x3(12 フレーム、10~12fps の炎/火炎)、1x20(20 フレームのストリップ、30fps のハチ)のシートを使います。
シミュレーション空間(ローカル vs ワールド)
PSS_LOCAL(デフォルト)— パーティクルはエミッターと一緒に移動します。エミッターが移動すると、パーティクルの群れも引きずられます。移動するエンティティのオーラや光輪のエフェクトに適しています。PSS_WORLD— パーティクルはスポーン時のワールド空間の位置にとどまるため、エミッターが移動すると、その後ろに軌跡が残ります。
Tween駆動のエミッターの場合(0,7-particle-system の「Moving Trail」で検証済み):PSS_LOCAL では、エンティティの移動中もパーティクルの群れがエンティティに付随します。スポーンしたパーティクルをその場にとどめて、持続する軌跡を作りたい場合は PSS_WORLD に切り替えてください。テストシーンでは PSS_LOCAL を使ってこの効果を示しています。必須条件としてではなく、望む見た目に合わせて空間を選んでください。
テクスチャフィールド
texture フィールドでは、Material と同じ Texture 型を使用します。一般的なケース(ローカルファイル):
texture: {
src: 'assets/Images/spark.png'
}
Texture の完全な形式では filterMode/wrapMode に対応していますが、パーティクルシステムで必要なのは通常 src だけです。パーティクルでの Avatar/Video テクスチャの使用は未検証のため、ファイルテクスチャを使用してください。
注意点
- テクスチャのパス — パーティクルテクスチャのデフォルトの場所は
assets/Images/です。Material テクスチャと同じパス規則を使用します。古いシーンではassets/scene/Images/またはassets/scene/textures/にある場合があります(引き続き動作します)。Creator Hub のアセットパックのテクスチャはassets/asset-packs/に、カスタムアイテムはassets/custom/に配置されるため、それらのパスをそのまま指定してください。 - クォータニオン回転フィールド —
rotationOverTimeは軸ごとの角速度として解釈されます(クォータニオン → Euler XYZ)。Quaternion.fromEulerDegrees(0, 90, 0)は Y 軸を 90°/s で回転させます。単位クォータニオンは回転しません。 additionalForceはワールド空間です。simulationSpace = PSS_LOCALの場合でも同様です。風や漂流の方向は、エミッターの回転にかかわらず一定です。limitVelocity.dampen = 1はハードクランプです(パーティクルがspeedを超えることはありません)。値を小さくすると、速度はいったん上限を超えた後に減衰します。colorOverTimeの終点でColor.alpha = 0にするのが、パーティクルをフェードアウトさせる標準的な方法です。sizeOverTimeの終点を 0 にするだけに頼らないでください。- 評価時点では、バーストは
active = falseを無視します。連続レートに対してactiveが確認されるのはフレームごとに 1 回です。バースト時刻の後に設定しても、そのバーストはさかのぼってキャンセルされません。 - エミッターのエンティティにメッシュをアタッチする必要はありません —
MeshRendererの追加は無関係です。パーティクルは ParticleSystem コンポーネント自体によってレンダリングされます。
パフォーマンス
lifetime * rateを低く保ってください。この積が、定常状態でのパーティクルの生存数です。- 表示範囲外では、
playbackState = PS_STOPPEDまたはactive = falseを使ってシステムを無効にしてください。 - 不透明/半透明のエフェクトには
PSB_ALPHAを優先してください。PSB_ADDは発光/炎のエフェクトに最適ですが(視覚的に重なります)、加算合成を複数レイヤーに適用するケースが最もコストが高くなります。 - スプライトシートは低コストです。1 つのシステムで複数のテクスチャを使うことはできません(1 システムにつき 1 テクスチャ)。
リソース
{baseDir}/references/particle-presets.md— SDK7 テストシーンの、すぐに使える 17 種類のプリセット設定(炎、魔法、降雪、渦、噴水、群れ、火花、雨、バースト、花火、キャンプファイヤー、軌跡)。- ライブチューナー:
ParticleLab.dcl.eth(Decentraland client で開きます)。
サンプルシーン
https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/0,7-particle-system— エンジンチームによる正確な実装例:上記の 17 種類のプリセット、すべての形状(Point/Sphere/Cone/Box)、PSB_ADD/PSB_ALPHA、連続的なrateとbursts(単発およびループする花火)、spriteSheet(4×4/4×3/1×20)、limitVelocity、billboard: falseでのinitialRotation/rotationOverTime、faceTravelDirection、additionalForce、およびsimulationSpaceを実証する Tween 駆動のエミッター。