Ezelia / EPSy

a Particles system for Phaser and Pixi
41 stars 15 forks source link

Some particles won't die #4

Open Tumetsu opened 9 years ago

Tumetsu commented 9 years ago

I'm having a problem with your particlesystem on Phaser.js. I created a flame particle much like yours example on and added it to my game. I'm adjusting its emissionRate on runtime depending on player interaction. It works fine, however occasionally particles get "stuck" and don't move anymore and won't disappear: example

Emitter's particleCount is showing 0 and those leftover particles only disappear after they get recycled I guess. Any idea of the solution? Is there a way to "force clear" particles?

alaa-eddine commented 9 years ago

Hi, It's difficult to know what's the problem based only on the description. does the problem only occure with Phaser ? or also in EPSy online editor ?

Tumetsu commented 9 years ago

I couldn't reproduce it on online editor, only on Phaser. If you want live demo of it, you can check this Heroku app of my project: https://iamspaceship.herokuapp.com/ Just click the button repeatedly and you should see stuck exhaust fumes. They disappear after a while since I turn particle visibilty off after a few seconds but they are noticeable.

If you want to inspect the particle emitter and its properties, you can check them on browser debug console with Lss.state.spaceship.flameParticles.

I suppose the problem could stem from how I frequently adjust the emissionRate (based on clicks).

alaa-eddine commented 9 years ago

actually, if you dynamically adjust the emission rate, when you go from a higher emittion to a lower one, I suppose that some particles from the older emission stick in an undefined state.

when you change the emission rate, you should make sure to reinit the whole particle emitter to avoir this situation, or loop throught all particles and kill them.

maybe there is a better solution to do make the emitter support it, but I don't know what can actually be the better solution for such situations.

Tumetsu commented 9 years ago

Makes sense. Could you elaborate how I could remove the particles? Where are they stored? I think that I could run a script to remove them when count reaches zero.

alaa-eddine commented 9 years ago

the particle emitter have a restart() method which will reinitialize all particles. but depending on your code, if you are using Pixi or Phaser plugins, the emitters are wrapped inside the plugin instance. if you are using the default export, the code to restart the emitters should be like this

// epsy instantiation

for (var i=0; i<epsy.emitters.length; i++)
{
    epsy.emitters[i].restart();
}

if you didn't used the export (so you created your emitter using something like this:

var emitter = new EPSY.Emitter(config);

then you simply need to call restart() for each emitter instance you'v created

emitter.restart()
Tumetsu commented 9 years ago

Hmm, I tried to call emitter.restart() after particleCount reached 0. Doesn't seem to clear the leftover particles.

alaa-eddine commented 9 years ago

Can you share the piece of code where you are changing the emission rate and where you are calling restart ?

Tumetsu commented 9 years ago

Sure. Here is my whole "class" so you can see the whole structure. Restart happens in update(). I already verified that the if-statement is evaluated and restart is called.

Lss.Spaceship = function(game, x, y)
{
    Phaser.Sprite.call(this, game, x, y, 'playership');
    this.anchor.setTo(0.5, 0.5);
    game.communication.inComingSignal.add(this.dataListener, this);
    game.communication.pushedButtonSignal.add(this.pushedButtonListener, this);

    //creating a particle system from a given configuration
    this.flameParticles = this.game.epsy.loadSystem(game.cache.getJSON("flameparticleconf"), -this.width/2 - 8, 0);
    this.addChild(this.flameParticles);

    game.physics.arcade.enable(this);
    this.body.collideWorldBounds = true;
}

//inheritance
Lss.Spaceship.prototype = Object.create(Phaser.Sprite.prototype);
Lss.Spaceship.prototype.constructor = Lss.Spaceship ;

Lss.Spaceship.prototype.update = function() {
    this.flameParticles.update();

    if (this.flameParticles.getChildAt(0).emitter._particleCount == 0) {
        this.flameParticles.getChildAt(0).emitter.restart();
    }

};

Lss.Spaceship.prototype.dataListener = function(data) {
    this.flameParticles.getChildAt(0).emitter.emissionRate = Phaser.Math.clamp(data["newPushForce"]/10, [0, 200]);

};

//Player pushed the button in the client
Lss.Spaceship.prototype.pushedButtonListener = function(force) {
    this.flameParticles.getChildAt(0).emitter.emissionRate += Phaser.Math.clamp(force/10, [0, 200]);

};
alaa-eddine commented 9 years ago

well actually, calling restart when the particle count is zero will have no effect, because particles are allways present. try calling restart here

Lss.Spaceship.prototype.pushedButtonListener = function(force) {
    this.flameParticles.getChildAt(0).emitter.emissionRate += Phaser.Math.clamp(force/10, [0, 200]);
    this.flameParticles.getChildAt(0).emitter.restart();
};
Tumetsu commented 9 years ago

Doesn't do the trick. It restarts when the player clicks the button (and I want to add particles, not restart them. Actually, is there any kind of "burst" concept in the engine?) also it still leaves some leftover particles if I click repeatedly and then stop. It would be ok for me to burst particles on every click of the button.

Anyway, what really happens during rendering of particles? I understood that _particleCount and particle arrays would reference to each particle. So how they can reach zero while there is still visible particles?

alaa-eddine commented 9 years ago

if you want to add particle better use emitter.totalParticles = new_value but this will restart the emitter.

All the emitter logic is here : https://github.com/Ezelia/EPSy/blob/master/core/Emitter.class.ts the rendering is separated. the main rendering loop will call emitter.update(deltaTime) which will call updateParticle for each particle. the function update particle properties, or recycle it if it's dead. I think that the recycling condition fail for some reason when the emission rate change in runtime.

Tumetsu commented 9 years ago

Yeah, the totalParticles doesn't work for me since restart deletes existing particles.

What I want to achieve is to have the particles represent the clickrate of the user. So if user clicks 10 times/s there is larger exhaust fume than when user clicks only 2 times/s. This is why I need to be able to add new particles to the existing ones, otherwise the long trail of the fume disappears. In past I have used particle systems which had concepts of burst and stream. In your system I suppose this is analog to changing emission rate on runtime?

Thanks for answers really. I'll try to look into emitter and try to figure out how to solve this. I haven't looked into your code yet but I think that like you said there is something with recycle condition which basically deletes particle references from array but doesn't remove them from canvas.

alaa-eddine commented 9 years ago

No there is no burst feature in the emitter, I thought that emissionrate + particle lifetime was enought for such emitter, but I'm maybe wrong :)

The emitter is mainly based on this tutorial http://buildnewgames.com/particle-systems/ I refactored the code and added some configuration features.

my motivation was to bring an easy way to make particle systems for Pixi and Phaser, so I worked on the editor more than the emitter itself (I need to find time to release editor source code btw :D )

I maybe need to add some methods to the emitter allowing clean parameters change in runtime.

Tumetsu commented 9 years ago

I might have mistaken but it seems to me that emissionrate + particle lifetime wouldn't work for me since I need the "bursts" to add up to create a bigger exhaustion fume which won't work if I have to restart the emitter when I change emissionrate. I suppose I could try to juggle with several emitters.

That being said, other than this issue your system is really nice and I'm glad I found it since Phaser didn't seem to offer anything good enough for me out of the box.

alaa-eddine commented 9 years ago

Today I was playing again with the emitter to update it to the latest Pixi version, and I found that I already made a function to reset the emitter :) hopefully it'll fix your issue.

in your code, when you need to reset the emitter call this function


this.game.epsy.particleRenderer.reset();

I will add this function to epsy object directly in the next code update :)