Using a loop to cleanup all particles?

Hi,



When adding various particles to my code, I noticed that the particles don’t seem to have a burst/single shot option (that I noticed, I may be wrong and someone will probably point this out for me :P), so I tried to write a cleanup loop that runs every now and then and clears them.



I’m doing this by adding them to an ArrayList of my custom class, and going through that and deleting it every now and then, but the problem is, it’s not adding anything to the ArrayList…



Here is the code for the particle_system (the particle generators):

Code:
public class particle_system { static ArrayList<particle_system> cleanupl = new ArrayList(); static long time; static long lasttime; static String name; ParticleEmitter newC; public static void initClean() { lasttime = System.currentTimeMillis(); } public static void cleanup(float tpf) { time = System.currentTimeMillis(); if(time > lasttime + 2500) { lasttime = time; System.out.println("cleaning parts at " + time + ", clean size:" + cleanupl.size()); for(int i = 0; i < cleanupl.size(); i++) { System.out.println("cleaning: " + cleanupl.get(i).getname()); cleanupl.get(i).end(); cleanupl.get(i).kill(); } } cleanupl.clear(); }
public void start()
{
    newC.emitAllParticles();
}

public void end()
{
    newC.killAllParticles();
}

public void kill()
{
    newC.removeFromParent();
}

public String getname()
{
    return name;
}

public static class custom_particle extends particle_system
{
    String texture;
    ColorRGBA color;
    String name;
    Vector3f pos;
    
    public custom_particle(String part, ColorRGBA col, String na, Vector3f po)
    {
        texture = part;
        color = col;
        name = na;
        pos = po;
    }
    
    public void create()
    {
        newC = new ParticleEmitter(&quot;em:&quot;+name,ParticleMesh.Type.Triangle,180);
        Material particle_skin = new Material(Main.api.getAssetManager(),&quot;Common/MatDefs/Misc/Particle.j3md&quot;);
        particle_skin.setTexture(&quot;Texture&quot;, Main.api.getAssetManager().loadTexture(texture));
        newC.setMaterial(particle_skin);
        newC.setImagesX(3); newC.setImagesY(3);
        newC.setEndColor(color);
        newC.setStartColor(color.mult(2));
        newC.getParticleInfluencer().setInitialVelocity(new Vector3f(0,50,0));
        newC.setStartSize(3f);
        newC.setEndSize(0.1f);
        newC.setGravity(10,35,10);
        newC.setLowLife(1f);
        newC.setHighLife(1.5f);
        newC.getParticleInfluencer().setVelocityVariation(0.3f);
        Main.rn.attachChild(newC);
        newC.setLocalTranslation(pos);
        cleanupl.add(this);
        
    }
}

public static class particle_fire_small extends particle_system
{
    ParticleEmitter newB;
    String texture = &quot;Textures/fireparticle.png&quot;;
    ColorRGBA color;
    String name;
    Vector3f pos;
    
    public particle_fire_small(String namei, Vector3f posi, ColorRGBA col)
    {
        name = namei;
        pos = posi;
        color = col;
    }
    
    public void create()
    {
        newC = new ParticleEmitter(&quot;em:&quot;+name,ParticleMesh.Type.Triangle,300);
        newB = new ParticleEmitter(&quot;em2:&quot;+name,ParticleMesh.Type.Triangle,100);
        Material particle_skin = new Material(Main.api.getAssetManager(),&quot;Common/MatDefs/Misc/Particle.j3md&quot;);
        particle_skin.setTexture(&quot;Texture&quot;, Main.api.getAssetManager().loadTexture(texture));
        Material particle_skin2 = new Material(Main.api.getAssetManager(),&quot;Common/MatDefs/Misc/Particle.j3md&quot;);
        particle_skin2.setTexture(&quot;Texture&quot;, Main.api.getAssetManager().loadTexture(texture));
        particle_skin2.setBoolean(&quot;m_PointSprite&quot;, true);
        newC.setMaterial(particle_skin);
        newB.setMaterial(particle_skin2);
        newC.setImagesX(2); newC.setImagesY(2);
        newB.setImagesX(2); newB.setImagesY(2);
        newC.setEndColor(color);
        newB.setEndColor(ColorRGBA.DarkGray);
        newC.setStartColor(color);
        newB.setStartColor(ColorRGBA.DarkGray);
        newC.getParticleInfluencer().setInitialVelocity(new Vector3f(0,1,0));
        newB.getParticleInfluencer().setInitialVelocity(new Vector3f(0,1,0));
        newC.addLight(Main.lights.playerlight);
        //newB.addLight(Main.lights.playerlight);
        newC.setStartSize(1.5f);
        newB.setStartSize(1.25f);
        newC.setEndSize(0.1f);
        newB.setEndSize(0.1f);
        newC.setGravity(0,-25,0);
        newB.setGravity(0f, -10f, 0f);
        newC.setLowLife(0.45f);
        newB.setLowLife(0.10f);
        newC.setHighLife(0.85f);
        newB.setHighLife(0.75f);
        newC.getParticleInfluencer().setVelocityVariation(0.25f);
        newB.getParticleInfluencer().setVelocityVariation(0.45f);
        Main.rn.attachChild(newC);
        Main.rn.attachChild(newB);
        newC.setLocalTranslation(pos);
        newB.setLocalTranslation(pos);
        cleanupl.add(this);
    }
}

public static class particle_fire_large extends particle_system
{
    ParticleEmitter newB;
    String texture = &quot;Textures/fireparticle.png&quot;;
    String texture_2 = &quot;Textures/smokeparticle.png&quot;;
    ColorRGBA color;
    String name;
    Vector3f pos;
    
    public particle_fire_large(String namei, Vector3f posi, ColorRGBA col)
    {
        name = namei;
        pos = posi;
        color = col;
    }
    
    public void create()
    {
        newC = new ParticleEmitter(&quot;em:&quot;+name,ParticleMesh.Type.Triangle,300);
        newB = new ParticleEmitter(&quot;em2:&quot;+name,ParticleMesh.Type.Triangle,100);
        Material particle_skin = new Material(Main.api.getAssetManager(),&quot;Common/MatDefs/Misc/Particle.j3md&quot;);
        particle_skin.setTexture(&quot;Texture&quot;, Main.api.getAssetManager().loadTexture(texture));
        Material particle_skin2 = new Material(Main.api.getAssetManager(),&quot;Common/MatDefs/Misc/Particle.j3md&quot;);
        particle_skin2.setTexture(&quot;Texture&quot;, Main.api.getAssetManager().loadTexture(texture_2));
        particle_skin2.setBoolean(&quot;m_PointSprite&quot;, true);
        newC.setMaterial(particle_skin);
        newB.setMaterial(particle_skin2);
        newC.setImagesX(2); newC.setImagesY(2);
        newB.setImagesX(2); newB.setImagesY(2);
        newC.setEndColor(color);
        newB.setEndColor(ColorRGBA.DarkGray);
        newC.setStartColor(color);
        newB.setStartColor(ColorRGBA.DarkGray);
        newC.setParticlesPerSec(60f);
        newC.setRotateSpeed(1f);
        newC.getParticleInfluencer().setInitialVelocity(new Vector3f(0.9f,0.9f,0.9f));
        newC.setShape(new EmitterBoxShape(new Vector3f(1,-1,1),new Vector3f(4,-1,4)));
        newB.getParticleInfluencer().setInitialVelocity(new Vector3f(0.2f,0.2f,0.2f));
        newB.setShape(new EmitterBoxShape(new Vector3f(1,-1,1),new Vector3f(4,-1,4)));
        newC.addLight(Main.lights.playerlight);
        //newB.addLight(Main.lights.playerlight);
        newC.setStartSize(3f);
        newB.setStartSize(2.5f);
        newC.setEndSize(0.05f);
        newB.setEndSize(0.25f);
        newC.setGravity(0,-25,0);
        newB.setGravity(0f, -10f, 0f);
        newC.setLowLife(0.10f);
        newB.setLowLife(0.75f);
        newC.setHighLife(1.0f);
        newB.setHighLife(2.0f);
        newC.getParticleInfluencer().setVelocityVariation(0f);
        newB.getParticleInfluencer().setVelocityVariation(1.5f);
        Main.rn.attachChild(newC);
        Main.rn.attachChild(newB);
        newC.setLocalTranslation(pos.subtract(new Vector3f(3,-1,3)));
        newB.setLocalTranslation(pos.subtract(new Vector3f(3,-1,3)));
        cleanupl.add(this);
    }
}

public static class dustpuff extends particle_system
{
    String texture = &quot;Textures/dustparticle.png&quot;;
    ColorRGBA color;
    String name;
    Vector3f pos;
    
    public dustpuff(String namei, Vector3f posi, ColorRGBA col)
    {
        name = namei;
        pos = posi;
        color = col;
    }
    
    public void create()
    {
        newC = new ParticleEmitter(&quot;em:&quot;+name,ParticleMesh.Type.Triangle,150);
        Material particle_skin = new Material(Main.api.getAssetManager(),&quot;Common/MatDefs/Misc/Particle.j3md&quot;);
        particle_skin.setTexture(&quot;Texture&quot;, Main.api.getAssetManager().loadTexture(texture));
        newC.setMaterial(particle_skin);
        newC.setImagesX(1); newC.setImagesY(1);
        newC.setEndColor(color);
        newC.setStartColor(color);
        newC.setParticlesPerSec(60f);
        newC.setRotateSpeed(1f);
        newC.getParticleInfluencer().setInitialVelocity(new Vector3f(0.1f,1.3f,0.1f));
        newC.addLight(Main.lights.playerlight);
        newC.setStartSize(9f);
        newC.setEndSize(0.05f);
        newC.setGravity(0,-10,0);
        newC.setLowLife(0.10f);
        newC.setHighLife(1.0f);
        newC.getParticleInfluencer().setVelocityVariation(5f);
        Main.rn.attachChild(newC);
        newC.setLocalTranslation(pos);
        cleanupl.add(this);
    }
}

}


Here is the part of my object_player where the projectile is created and shot, which spawns the dustpuff particle on hit:
Code:
else if (binding.equals("shoot") && !keyPressed) { Spatial spa = Main.api.getAssetManager().loadModel("Models/arrow.obj"); Spatial sphg = Main.spawn.create_model(Main.api, Vector3f.ZERO, spa, "01ddf"); sphg.setLocalTranslation(Main.play.getLocation().add(Main.play.cam.getDirection().mult(5))); sphg.rotate(Main.play.cam.getRotation()); RigidBodyControl sphc = new projectile(new SphereCollisionShape(0.6f),150); sphc.setCcdMotionThreshold(0.1f); sphc.setLinearVelocity(Main.play.cam.getDirection().mult(450)); sphg.addControl(sphc); Main.rn.attachChild(sphg); physics_controller.add_child(sphc); }

And here is the projectile code:
Code:
public class projectile extends RigidBodyControl implements PhysicsCollisionListener, PhysicsTickListener { public projectile(){}
public projectile(CollisionShape shape, float mass)
{
    super(shape,mass);
}

@Override
public void setPhysicsSpace(PhysicsSpace space) {
    super.setPhysicsSpace(space);
    if (space != null) {
        space.addCollisionListener(this);
    }
}

public void collision(PhysicsCollisionEvent event)
{
    if(space == null)
    {
        return;
    }
    if(event.getObjectA() == this || event.getObjectB() == this)
    {
        if(event.getObjectA() == this)
        {
            event.getNodeA().removeControl(this);
            dustpuff bop = new dustpuff(&quot;arrowpuff&quot;, event.getPositionWorldOnA(), ColorRGBA.Brown);
            bop.create();
            bop.start();
        }
        if(event.getObjectB() == this)
        {
            event.getNodeB().removeControl(this);
            dustpuff bop = new dustpuff(&quot;arrowpuff&quot;, event.getPositionWorldOnB(), ColorRGBA.Brown);
            bop.create();
            bop.start();
        }
        space.addTickListener(this);
        space.remove(this);
    }
}

public void physicsTick(PhysicsSpace space, float f)
{
    space.removeCollisionListener(this);
    space.removeTickListener(this);
}

public void prePhysicsTick(PhysicsSpace space, float f)
{
    space.removeCollisionListener(this);
}

@Override
public void update(float tpf)
{
    super.update(tpf);
}

}


Basically, to explain it in short, I have a projectile, the player shoots it from the camera's position, it hits the ground and spawns the dust particles, but the dust particles don't go away. For my cleanup, I made an arraylist of the particle_system type but it refuses to get added to it for cleanup, and I'm not sure what I did wrong or how to fix it.

Help is appreciated :D.

Set the particle per second to 0 on the dust particles emitter and use emittAllPatricles() method to fire them all at once.

should be

[java]

emitter.setParticlesPerSecond(0);

[/java]

and

[java]

emitter.emittAllParticles();

[/java]

1 Like

That worked perfectly, thanks!



Although, in the case I have it, wouldn’t it leave rogue emitter nodes around? (I thought setting the particles per second to 0 would make it not even emit at all, I’m completely new to them)



Also, for the sake of performance, does anyone know how to get that loop working, or is there a better way to clear/remove all particles from a list after a certain time?

Manage it with a control.

You can create some kind of DisposeControl, that handle the removal from the scene graph, and add it to the emitter.

Let say you want the dust to be visible 5 seconds, tweak your emitter so the dust fades at 5 sec.

The control on its side will just wait 5 seconds (you can stack tpf to have the time) and at 5 seconds it removes the emitter from the scene graph and everything should be garbage collected if you didn’t keep any reference to this emitter.

1 Like

How would I create a control? Just create a class and extend/implement Control or something? I’m new to the more complicated ends of optimization and all that in programming.

yeah just create a class. I recommend extending AbstractControl instead of implementing Control because it has convenient enable/disable feature already implemented. You’ll have a onControlUpdate(float tpf) method. That’s where you’ll be adding your code.



There are 2 other methods onControlRender() and cloneForSpatial, but ignore them for now and just return null for cloneForSpatial.

1 Like

Thank you, I’ll have a go at that and see how it goes :slight_smile:

Okay, so I made a destroy control like this:

Code:
/** * * @author Ben */ public class DestroyControl extends AbstractControl { static long time = System.currentTimeMillis(); static long lasttime = System.currentTimeMillis(); @Override protected void controlUpdate(float tpf) { time = System.currentTimeMillis(); if(time > lasttime + 2500) { this.getSpatial().removeFromParent(); } }
@Override
protected void controlRender(RenderManager rm, ViewPort vp)
{
    
}

public Control cloneForSpatial(Spatial spatial)
{
    return null;
}

}


And I add it to my particles, it works fine, but when it only works once. The first projectile spawns particles when it hits, then they are removed as they should be, and then subsequent projectiles no longer spawn particles on hit.

I'm adding the control to the particles like this:
Code:
DestroyControl d = new DestroyControl(); newC.addControl(d);

The above was added into the create function of the dustpuff particle_system.

Not sure what's going on.. I'm really not good with the whole instanced stuff in java, and if there is an interaction going on here I'm not aware of, it'd be appreciated if someone could explain it to me :(.
@koaandl said:
Okay, so I made a destroy control like this:
Code:
public class DestroyControl extends AbstractControl { static long time = System.currentTimeMillis(); static long lasttime = System.currentTimeMillis();

And I add it to my particles, it works fine, but when it only works once.


The static thing is tripping you up. Static in java in this case means that the variable lives on the class, not the instance. In effect, all your Destroyers share the same variable and they interfere with each other. Just remove the static keyword and things will probably work.
1 Like

Ohhh, I never quite got how static worked, I’ll have a try at that, thanks man.

@koaandl you’ll be better of simply accumulating tpf as @nehon suggested …

@nehon said:
... (you can stack tpf to have the time) ...


roughly like this ...

[java]
private float count = 0;
@Override
protected void controlUpdate(float tpf) {
count+=tpf;
if (count > 2500) {
// do whatever
}
}

[/java]
1 Like

Oh, thanks, I’ll try that!

Thanks guys, it works perfectly now after using all of your suggestions, and I have a much better understanding of static variables. Much appreciated everyone :).

1 Like