Within ParticleEmitter if the setParticlesPerSec is called every frame then no particle will ever be emitted. This is because setting ParticlesPerSec resets timeDifference. I can see this was done (in commit 07a6ca9) to solve the problem that after ParticlesPerSec has been zero for a long time timeDifference becomes huge.
The reason someone may want to call setParticlesPerSec every tick is to produce a smooth change in intensity, e.g. a slowly dying fire,
Test case (if reflection is used to just set the parameter it works fine, using the setter doesn’t)
public class Main extends SimpleApplication {
ParticleEmitter fire;
float particlesPerSecond = 10;
boolean useReflectionHack = false;
@Override
public void simpleUpdate(float tpf) {
try {
particlesPerSecond -= tpf;
if (particlesPerSecond < 0) {
particlesPerSecond = 5;
}
if(useReflectionHack){
Field f = fire.getClass().getDeclaredField("particlesPerSec");
f.setAccessible(true);
f.set(fire, particlesPerSecond); //Works fine (in this use case)
}else{
fire.setParticlesPerSec(particlesPerSecond); //no particles ever emitted :(
}
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
public static void main(String[] args){
Main main = new Main();
main.start();
}
@Override
public void simpleInitApp() {
fire =
new ParticleEmitter("Emitter", ParticleMesh.Type.Triangle, 30);
Material mat_red = new Material(assetManager,
"Common/MatDefs/Misc/Particle.j3md");
mat_red.setTexture("Texture", assetManager.loadTexture(
"Effects/Explosion/flame.png"));
fire.setMaterial(mat_red);
fire.setImagesX(2);
fire.setImagesY(2); // 2x2 texture animation
fire.setEndColor( new ColorRGBA(1f, 0f, 0f, 1f)); // red
fire.setStartColor(new ColorRGBA(1f, 1f, 0f, 0.5f)); // yellow
fire.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 2, 0));
fire.setStartSize(1.5f);
fire.setEndSize(0.1f);
fire.setGravity(0, 0, 0);
fire.setLowLife(1f);
fire.setHighLife(3f);
fire.getParticleInfluencer().setVelocityVariation(0.3f);
fire.setParticlesPerSec(particlesPerSecond);
rootNode.attachChild(fire);
}
}
Instead could timeDifference be capped when setParticlesPerSec is called such that at most it is just about to emit a particle. So the new setParticlesPerSec would be
public void setParticlesPerSec(float particlesPerSec) {
this.particlesPerSec = particlesPerSec;
timeDifference = Math.min(timeDifference,1f / particlesPerSec); //prevent large accumulated timeDifference from causing a huge number of particles to be emitted
}
Notes:
Original commit discussed here ParticleEmitter particlesPerSecond set to 0 accumulates timeDiff
Original commit * Fix "particlesPerSecond set to 0 accumulates timeDiff" issue · jMonkeyEngine/jmonkeyengine@07a6ca9 · GitHub