Skip to content

Particle Emitter cannot emit particles if its emissions per second is updated every frame #1113

@richardTingle

Description

@richardTingle

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 I think timeDifference should 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 https://hub.jmonkeyengine.org/t/particleemitter-particlespersecond-set-to-0-accumulates-timediff/23432

Original commit 07a6ca9

Thread https://hub.jmonkeyengine.org/t/particle-emitter-cannot-emit-particles-if-its-emissions-per-second-is-updated-every-frame/41930

I plan on fixing this myself

Metadata

Metadata

Assignees

No one assigned

    Labels

    defectSomething that is supposed to work, but doesn't. Less severe than a "bug"

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions