particle-systems
29
总安装量
29
周安装量
#7235
全站排名
安装命令
npx skills add https://github.com/pluginagentmarketplace/custom-plugin-game-developer --skill particle-systems
Agent 安装分布
claude-code
22
gemini-cli
20
opencode
20
codex
19
antigravity
14
cursor
14
Skill 文档
Particle Systems
Particle System Architecture
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â PARTICLE LIFECYCLE â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¤
â EMISSION â
â ââ Spawn Rate (particles/second) â
â ââ Burst (instant spawn count) â
â ââ Shape (point, sphere, cone, mesh) â
â â â
â SIMULATION â
â ââ Velocity (initial + over lifetime) â
â ââ Forces (gravity, wind, turbulence) â
â ââ Collision (world, depth buffer) â
â ââ Noise (procedural movement) â
â â â
â RENDERING â
â ââ Billboard (camera-facing quads) â
â ââ Mesh (3D geometry per particle) â
â ââ Trail (ribbon following path) â
â ââ GPU Instancing (batched draw) â
â â â
â DEATH â
â ââ Lifetime expired â recycle or destroy â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
Common Effect Recipes
EXPLOSION EFFECT:
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â LAYERS: â
â 1. Flash (instant, bright, 0.1s) â
â 2. Core fireball (expanding sphere, 0.3s) â
â 3. Debris (physics-enabled chunks, 1-2s) â
â 4. Smoke (slow rising, fade out, 2-3s) â
â 5. Sparks (fast, gravity-affected, 0.5-1s) â
â 6. Shockwave (expanding ring, 0.2s) â
â â
â SETTINGS: â
â ⢠Emission: Burst only (no rate) â
â ⢠Start speed: 5-20 (varies by layer) â
â ⢠Gravity: -9.8 for debris, 0 for smoke â
â ⢠Color: OrangeâRedâBlack over lifetime â
â ⢠Size: Start large, shrink (fireball) or grow (smoke) â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
FIRE EFFECT:
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â LAYERS: â
â 1. Core flame (upward, orange-yellow, looping) â
â 2. Ember particles (small, floating up) â
â 3. Smoke (dark, rises above flame) â
â 4. Light source (flickering point light) â
â â
â SETTINGS: â
â ⢠Emission: Continuous (50-100/sec) â
â ⢠Velocity: Upward (2-5 units/sec) â
â ⢠Noise: Turbulence for natural movement â
â ⢠Color: WhiteâYellowâOrangeâRed over lifetime â
â ⢠Size: Start small, grow, then shrink â
â ⢠Blend: Additive for glow effect â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
MAGIC SPELL EFFECT:
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â LAYERS: â
â 1. Core glow (pulsing, bright center) â
â 2. Orbiting particles (circle around core) â
â 3. Trail particles (follow movement path) â
â 4. Impact burst (on hit/destination) â
â 5. Residual sparkles (lingering after effect) â
â â
â SETTINGS: â
â ⢠Emission: Rate + burst on cast/impact â
â ⢠Velocity: Custom curves for orbiting â
â ⢠Trails: Enable for mystical streaks â
â ⢠Color: Themed to element (blue=ice, red=fire) â
â ⢠Blend: Additive for ethereal glow â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
Unity Particle System Setup
// â
Production-Ready: Particle Effect Controller
public class ParticleEffectController : MonoBehaviour
{
[Header("Effect Settings")]
[SerializeField] private ParticleSystem mainEffect;
[SerializeField] private ParticleSystem[] subEffects;
[SerializeField] private AudioSource audioSource;
[SerializeField] private Light effectLight;
[Header("Pooling")]
[SerializeField] private bool usePooling = true;
[SerializeField] private float autoReturnDelay = 3f;
private ParticleSystem.MainModule _mainModule;
private float _originalLightIntensity;
public event Action OnEffectComplete;
private void Awake()
{
_mainModule = mainEffect.main;
if (effectLight != null)
_originalLightIntensity = effectLight.intensity;
}
public void Play(Vector3 position, Quaternion rotation)
{
transform.SetPositionAndRotation(position, rotation);
// Play all particle systems
mainEffect.Play();
foreach (var effect in subEffects)
{
effect.Play();
}
// Play audio
if (audioSource != null)
audioSource.Play();
// Animate light
if (effectLight != null)
StartCoroutine(AnimateLight());
// Auto-return to pool
if (usePooling)
StartCoroutine(ReturnToPoolAfterDelay());
}
public void Stop()
{
mainEffect.Stop(true, ParticleSystemStopBehavior.StopEmitting);
foreach (var effect in subEffects)
{
effect.Stop(true, ParticleSystemStopBehavior.StopEmitting);
}
}
private IEnumerator AnimateLight()
{
effectLight.intensity = _originalLightIntensity;
effectLight.enabled = true;
float duration = _mainModule.duration;
float elapsed = 0f;
while (elapsed < duration)
{
elapsed += Time.deltaTime;
float t = elapsed / duration;
effectLight.intensity = Mathf.Lerp(_originalLightIntensity, 0f, t);
yield return null;
}
effectLight.enabled = false;
}
private IEnumerator ReturnToPoolAfterDelay()
{
yield return new WaitForSeconds(autoReturnDelay);
OnEffectComplete?.Invoke();
// Reset for reuse
Stop();
if (effectLight != null)
{
effectLight.enabled = false;
effectLight.intensity = _originalLightIntensity;
}
}
public void SetColor(Color color)
{
var startColor = _mainModule.startColor;
startColor.color = color;
_mainModule.startColor = startColor;
if (effectLight != null)
effectLight.color = color;
}
public void SetScale(float scale)
{
transform.localScale = Vector3.one * scale;
}
}
GPU Particles (Compute Shader)
// â
Production-Ready: GPU Particle Compute Shader
#pragma kernel UpdateParticles
struct Particle
{
float3 position;
float3 velocity;
float4 color;
float size;
float lifetime;
float maxLifetime;
};
RWStructuredBuffer<Particle> particles;
float deltaTime;
float3 gravity;
float3 windDirection;
float windStrength;
float turbulenceStrength;
float time;
float noise3D(float3 p)
{
// Simple 3D noise for turbulence
return frac(sin(dot(p, float3(12.9898, 78.233, 45.164))) * 43758.5453);
}
[numthreads(256, 1, 1)]
void UpdateParticles(uint3 id : SV_DispatchThreadID)
{
Particle p = particles[id.x];
if (p.lifetime <= 0)
return;
// Apply forces
float3 acceleration = gravity;
// Wind
acceleration += windDirection * windStrength;
// Turbulence
float3 turbulence = float3(
noise3D(p.position + time) - 0.5,
noise3D(p.position + time + 100) - 0.5,
noise3D(p.position + time + 200) - 0.5
) * turbulenceStrength;
acceleration += turbulence;
// Update velocity and position
p.velocity += acceleration * deltaTime;
p.position += p.velocity * deltaTime;
// Update lifetime
p.lifetime -= deltaTime;
// Fade out
float lifetimeRatio = p.lifetime / p.maxLifetime;
p.color.a = lifetimeRatio;
p.size *= (0.5 + 0.5 * lifetimeRatio);
particles[id.x] = p;
}
Performance Optimization
PARTICLE BUDGET GUIDELINES:
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â PLATFORM â MAX PARTICLES â MAX DRAW CALLS â NOTES â
ââââââââââââââââââ¼ââââââââââââââââ¼âââââââââââââââââ¼âââââââââââ¤
â Mobile Low â 500 â 5 â Simple â
â Mobile High â 2,000 â 10 â Moderate â
â Console â 50,000 â 50 â Complex â
â PC Low â 10,000 â 20 â Moderate â
â PC High â 1,000,000+ â 100+ â GPU sim â
â VR â 5,000 â 15 â 90 FPS! â
ââââââââââââââââââ´ââââââââââââââââ´âââââââââââââââââ´âââââââââââ
OPTIMIZATION TECHNIQUES:
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â REDUCE COUNT: â
â ⢠LOD system (fewer particles at distance) â
â ⢠Cull off-screen emitters â
â ⢠Limit max particles per system â
â â
â REDUCE OVERDRAW: â
â ⢠Use smaller particle sizes â
â ⢠Reduce transparency layers â
â ⢠Use cutout instead of transparent â
â â
â BATCH DRAWS: â
â ⢠Share materials between systems â
â ⢠Use texture atlases â
â ⢠Enable GPU instancing â
â â
â GPU SIMULATION: â
â ⢠Use compute shaders for >10K particles â
â ⢠Move physics to GPU â
â ⢠VFX Graph (Unity) / Niagara (Unreal) â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
Effect Pooling System
// â
Production-Ready: VFX Pool Manager
public class VFXPoolManager : MonoBehaviour
{
public static VFXPoolManager Instance { get; private set; }
[System.Serializable]
public class EffectPool
{
public string effectName;
public ParticleEffectController prefab;
public int initialSize = 5;
public int maxSize = 20;
}
[SerializeField] private EffectPool[] effectPools;
private Dictionary<string, Queue<ParticleEffectController>> _pools;
private Dictionary<string, EffectPool> _poolConfigs;
private void Awake()
{
Instance = this;
InitializePools();
}
private void InitializePools()
{
_pools = new Dictionary<string, Queue<ParticleEffectController>>();
_poolConfigs = new Dictionary<string, EffectPool>();
foreach (var pool in effectPools)
{
_pools[pool.effectName] = new Queue<ParticleEffectController>();
_poolConfigs[pool.effectName] = pool;
// Pre-warm pool
for (int i = 0; i < pool.initialSize; i++)
{
var effect = CreateEffect(pool);
_pools[pool.effectName].Enqueue(effect);
}
}
}
private ParticleEffectController CreateEffect(EffectPool pool)
{
var effect = Instantiate(pool.prefab, transform);
effect.gameObject.SetActive(false);
effect.OnEffectComplete += () => ReturnToPool(pool.effectName, effect);
return effect;
}
public ParticleEffectController SpawnEffect(
string effectName,
Vector3 position,
Quaternion rotation)
{
if (!_pools.ContainsKey(effectName))
{
Debug.LogError($"Effect pool '{effectName}' not found!");
return null;
}
var pool = _pools[effectName];
ParticleEffectController effect;
if (pool.Count > 0)
{
effect = pool.Dequeue();
}
else if (_poolConfigs[effectName].maxSize > pool.Count)
{
effect = CreateEffect(_poolConfigs[effectName]);
}
else
{
Debug.LogWarning($"Pool '{effectName}' exhausted!");
return null;
}
effect.gameObject.SetActive(true);
effect.Play(position, rotation);
return effect;
}
private void ReturnToPool(string effectName, ParticleEffectController effect)
{
effect.gameObject.SetActive(false);
_pools[effectName].Enqueue(effect);
}
}
// Usage
public class WeaponController : MonoBehaviour
{
public void OnHit(Vector3 hitPoint, Vector3 hitNormal)
{
VFXPoolManager.Instance.SpawnEffect(
"ImpactSparks",
hitPoint,
Quaternion.LookRotation(hitNormal));
}
}
ð§ Troubleshooting
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â PROBLEM: Particles popping in/out â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¤
â SOLUTIONS: â
â â Add fade-in at birth (size/alpha over lifetime) â
â â Increase soft particle distance â
â â Check culling settings â
â â Extend lifetime with fade-out â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â PROBLEM: Low frame rate with particles â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¤
â SOLUTIONS: â
â â Reduce max particle count â
â â Use LOD (fewer particles at distance) â
â â Switch to GPU simulation â
â â Reduce overdraw (smaller/fewer particles) â
â â Use simpler shaders â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â PROBLEM: Particles clipping through geometry â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¤
â SOLUTIONS: â
â â Enable collision module â
â â Use depth fade/soft particles â
â â Adjust near clip plane â
â â Position emitter away from surfaces â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â PROBLEM: Effect looks different in build â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¤
â SOLUTIONS: â
â â Check quality settings (particle raycast budget) â
â â Verify shader compatibility â
â â Test on target hardware â
â â Check for editor-only components â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
Engine-Specific Tools
| Engine | System | GPU Support | Visual Editor |
|---|---|---|---|
| Unity | Shuriken | VFX Graph | Yes (VFX Graph) |
| Unity | VFX Graph | Native | Yes |
| Unreal | Cascade | Limited | Yes |
| Unreal | Niagara | Native | Yes |
| Godot | GPUParticles | Yes | Inspector |
Use this skill: When creating visual effects, polishing gameplay, or optimizing particle performance.