A particle system, much like the UI, is a Boo script. The script provides certain settings, and is run on each particle in a system.
There is a world-view particle limit, which defaults to 500.
There are three different key terms in terms in the particle system:
- Domain: The entire world's set of particles
- System: A system of particles, contained in the world, driven by a script
- Particle: An individual particle
Creating in plugins¶
To create a system, you inject the
IParticleDomain as a
and use its methods. Primarily the
CreateSystem method with a script
The returned object is a ref, and if the ref is lost, the particle
system will be destroyed. If that is undesirable, you can set
Pinned = true.
Empeld has a built-in particle system display. Similar to how you test UI's, simply write
empeld.exe --dev-psys path/to/my/particlesystem.psys
You can then use F5 to refresh, and WSQA to move around the system.
The particle itself is a set of fields, all of which are modifiable by the script.
|Pos||Vector3d||The real-world position of the particle|
|Vel||Vector3d||The current velocity of the particle|
|Acc||Vector3d||The current acceleration of the particle|
|TexU||Vector2||The U coordinate of the given texture. Defaults to (0,0)|
|TexV||Vector2||The V coordinate of the texture. Defaults to (1,1)|
|Col||Rgba||The color of the particle. Defaults to white (1,1,1,1)|
|Size||float||The size of the particle. Defaults to 1|
|Life||float||Current life of the particle. This is set automatically, but can be overriden. Ranges from 0 to 1|
|Lifespan||float||The life of a particle, in seconds|
|Child||int||The index of this particle as a child. eg. if we were the result of an exploding particle, our child would be 1. Defaults to 0|
|BlendMode||ParticleBlendMode||The mode with which the particle is blended (See below)|
* Undefined=0, * MaskAdd * MaskAlpha * Additive * Screen * Multiply
Unlike the UI scripts, which are duck-typed, the particle scripts have to use explicit types. This is because they need to be high-performance. You can read about explicit typing on the wiki, which you can find a link to on Boo
The script has various variables that can be assigned to it, some of which are optional.
|Texture||string||Yes||Relative path to an image of the particle|
|AudioSpawn||SoundDescriptor||Sound played when a particle is spawned|
|AudioChildSpawn||SoundDescriptor||Sound that's played when a child spawns|
|AudioDie||SoundDescriptor||Audio that is played when a particle dies|
|ParticleLife||float||Life of a particle in seconds. Default: 5|
|SystemLife||float||Life of a system in seconds. Default: Unlimited|
|SpawnRate||float||The delay, in seconds, until a new particle is spawned. Default 0.1|
|PerParticleBillboard||bool||If false, system computes the billboard angle (fast); if true, billboard is computed for each particle (slow)|
|Collision||bool||Whether or not we check for particle collision|
|DieOnCollision||bool||If true, particles will die on collision, otherwise, it calls the collide method or defaults to bouncing. Default true|
|AutoGravity||bool||If true, gravity is automatically applied to
|Wind||float||Coefficient of wind affect. Default 1.0|
The script also has a set of methods. Each of them have a default implementation, but you can provide them explicitly to customize behavior.
|constructor||This is just the boo class constructor|
|Simulate||Particle, float||Simulate the particle, for an elapsed time of
|Spawn||Particle||Called when a particle is spawned. Can initialize it here|
|Die||Particle, ret: int||Called when a particle dies. Returns the number of children that get spawned as an int|
|SpawnChild||Particle||Called when a particle child is spawned|
|NextSpawn||ret: float||Return a float to override the delay to the next child spawn|
|Collide||Particle||Called when a particle collides with something|
class ParticleController: Texture = "hardsphere.png" ParticleLife = 3f SpawnRate = 0.05f PerParticleBillboard = true AutoGravity = true Collision = true DieOnCollide = false static _rSeed = 1 r as Random = null def constructor(): r = Random(_rSeed) _rSeed += 1 def _random() as double: return r.NextDouble() * 2.0 - 1.0 def simulate(p as Particle, d as single): p.Vel += p.Acc * d p.Pos += p.Vel * d p.Col = Rgba(1f, 0.8f, 0.8f, 1f - p.Life) def collide(p as Particle): p.Vel = Vector3d(p.Vel.X, p.Vel.Y, p.Vel.Z * -0.8f) def spawn(p as Particle): p.Vel = Vector3d(_random()*2f, _random()*2f, 10f+_random()*2f ) p.Size = 0.05f p.Pos = Vector3d(0f, 0f, 0.55f)
class ParticleController: Texture = "hardsphere.png" #Audio: filename, gain, pitch, variance, min_dist, max_dist AudioSpawn = SoundDescriptor("firework_launch.wav", 0.5f, 1f, 0.3f, 2f, 30f) AudioChildSpawn = SoundDescriptor("firework_exp.wav", 2f, 1f, 0.3f, 10f, 50f) ParticleLife = 2f PerParticleBillboard = true static _rSeed = 1 r as Random = null def constructor(): r = Random(_rSeed) _rSeed += 1 def simulate(p as Particle, d as single): p.Vel += p.Acc * d p.Pos += p.Vel * d if p.Child > 0: p.Col = Rgba(p.Col.R, p.Col.G, p.Col.B, 1f - p.Life) def _random() as double: return r.NextDouble() * 2.0 - 1.0 def _getColor() as Rgba: return Rgba(r.NextDouble()*0.6f+0.2f, r.NextDouble()*0.6f+0.2f, r.NextDouble()*0.6f+0.2f, 1f) def spawn(p as Particle): p.Vel = Vector3d(_random() * 0.4f, _random() * 0.4f, r.NextDouble() * 10f + 25f) p.Acc = Vector3d(0f, 0f, -5f) p.Col = _getColor() p.Size = 0.5f def die(p as Particle): if p.Child == 0: return 30 return 0 def spawnChild(p as Particle): vInit = 5f p.Vel = Vector3d(_random()*vInit, _random()*vInit, _random()*vInit) p.Acc = Vector3d(0f, 0f, -2f) p.Size = 0.3f p.Lifespan = 2f def nextSpawn() as single: return r.NextDouble() * 3.5f