Jonny Shaw

PIXI Particles and subemitters

Recommended Posts

Hi all, was just having a play with an effect for an explosion, and thought it would be cool to trial out some fireball style effects with some sort of simulated gravity.

eg. 4 or 5 fireballs, with smoke trails behind..

Had a play but noticed that pixi particles doesn't really offer anything in terms of subemitters, unless I've missed it?  So really been trying to find a workaround.

I'm not so hot when it comes to the maths of any gravity, so have used gsaps 2dphysics plugin to get it working  https://greensock.com/docs/Plugins/Physics2DPlugin...
so each fireball itself is a sprite in a container, then onupdate with that animation, the x&y position of the fireball is sent to update the ownerposition of the trail emitter.

It does work pretty well appearance wise, but performance does feel like it takes a bit of a hit, which Im guessing maybe down to the plugin.  Any ideas on any more "optimal" ways in which something like this could be achieved?
 

{8E4C7319-9F27-4020-8F1C-1EC5218D428A}.png.jpg

Share this post


Link to post
Share on other sites

Aha, silly me see gravity can be simulated with the acceleration, so will try that :)  The only other issue I can see is accessing the fireball particles x,y position but hopefully children on the particle container, should give an option there

Share this post


Link to post
Share on other sites

Have managed to get this working, but had to find some workarounds and not sure if it's a bit complex, and could be optimised further...

Involved using a timer and a loop to emit manually, then track the last added fireball particles position (if I tried accessing as soon as it was emit, it would return a blank, because it seems emission rate of 0 even for one particle is not possible), so matched the delay to the emission rate and seems to work ok.

I'll likely add the particles back into particle containers now to improve performance a bit too.

TickerTimer is just a small util class I wrote that adds a callback function to the main app ticker with a delay (so works pretty much like setTimeout, but using the app ticker)

Any further ideas greatly appreciated!

 

import TickerTimer from "../utils/TickerTimer";

class Fireballs extends PIXI.Container{

    constructor(){
        super(...arguments);

        this.balls = [];
        this.trails = [];

        // Atlas...
        this.atlas = PIXI.loader.resources["./assets/fxAtlas.json"].textures;

        this.ballContainer = new PIXI.Container();
        this.trailContainer = new PIXI.Container();

        this.addChild(this.trailContainer);
        this.addChild(this.ballContainer);

        this.emitTimer = 0;
        this.emitRate = 6;

        this.fireballCount = 0;
        this.maxFireballs = 20;

        this.timer = new PIXI.ticker.Ticker();
        this.timer.stop();

        this.fireballs = new PIXI.particles.Emitter(
            this.ballContainer,
            [this.atlas["fireball1.psd"]],
            {
                "alpha": {
                    list: [
                        {value:0, time:0},
                        {value:1, time:0.2},
                        {value:1, time:0.8},
                        {value:0, time:1}
                    ],
                },
                "scale": {
                    list: [
                        {value:0, time:0},
                        {value:0.3, time:0.2},
                        {value:0.4, time:0.8},
                        {value:0.2, time:1}
                    ],
                    "minimumScaleMultiplier": 1
                },
                "color": {
                    "start": "#ffffff",
                    "end": "#ff6105"
                },
                "speed": {
                    "start": 800,
                    "end": 1000,
                    "minimumSpeedMultiplier": 1
                },
                "acceleration": {
                    "x": 0,
                    "y": this.randomRange(1000,2000)
                },
                "maxSpeed": 1000,
                "startRotation": {
                    "min": 160,
                    "max": 380
                },
                "noRotation": false,
                "rotationSpeed": {
                    "min": 500,
                    "max": 2000
                },
                "lifetime": {
                    "min": 1,
                    "max": 1
                },
                "blendMode": "add",
                "frequency": 0.1,
                "emitterLifetime": 0.2,
                "maxParticles": 10,
                "pos": {
                    "x": 0,
                    "y": 0
                },
                "addAtBack": false,
                "spawnType": "point"
            }
        );
        
        this.timer.add(this.emitLoop,this);

        this.timer.start();
    }

    emitLoop(deltaTime){
        this.emitTimer++;
        
        if(this.fireballCount < this.maxFireballs){
            if(this.emitTimer >= this.emitRate){
                this.emitTimer = 0;
                this.emitFireball();
            }
        }

        else {
            this.timer.remove(this.emitLoop, this);
            this.emitTimer = 0;
            this.fireballCount = 0;
        }
    }

    randomRange(min, max) {
        return Math.floor(Math.random() * (max - min + 1) + min);
      }

    emitFireball(){
        this.fireballCount++;
        if(this.fireballCount < this.maxFireballs){
            this.fireballs.autoUpdate = true;
            this.fireballs.playOnce(this.fireballDestroyed.bind(this));
            //this.fireballs.emit = true;
            let delayMe = new TickerTimer(this.timer, 0.2, this.createTrailSmoke.bind(this));
        }
    }
    
    fireballDestroyed(ball){
        this.balls.shift();
        this.trails.shift();
    }

    createTrailSmoke(){
        let fireball = this.ballContainer.children[this.ballContainer.children.length-1];       // Get the last added fireball.
        if(fireball!=null){
            let trailSmoke = this.createTrail(fireball.x, fireball.y,1);

            this.balls.push(fireball);
            this.trails.push(trailSmoke);
            window.Game.ticker.add(this.updateTrailSmoke, this);
        }
    }

    updateTrailSmoke(){
        for(let i = 0; i < this.balls.length; i++){
            this.trails[i].updateOwnerPos(this.balls[i].x, this.balls[i].y+20);
        }
    }

    createTrail(xPos, yPos, lifeSpan){
        let trail = new PIXI.particles.Emitter(
            this.trailContainer,
            [this.atlas["smoke3.psd"]],
            {
                "alpha": {
                    list: [
                        {value:0, time:0},
                        {value:1, time:0.2},
                        {value:0, time:1}
                    ],
                },
                "scale": {
                    list: [
                        {value:0.2, time:0},
                        {value:0.1, time:0.2},
                        {value:0.4, time:1}
                    ],
                    "minimumScaleMultiplier": 1
                },
                "color": {
                    list: [
                        {value:"#ffff5e", time:0},
                        {value:"#ff0000", time:0.2},
                        {value:"#333333", time:0.3},
                        {value:"#000000", time:1}
                    ]
                },
                "speed": {
                    "start": 0,
                    "end": 0,
                    "minimumSpeedMultiplier": 1
                },
                "acceleration": {
                    "x": 0,
                    "y": 100
                },
                "maxSpeed": 0,
                "startRotation": {
                    "min": 0,
                    "max": 360
                },
                "noRotation": false,
                "rotationSpeed": {
                    "min": 0,
                    "max": 800
                },
                "lifetime": {
                    "min": 0.6,
                    "max": 0.8
                },
                "blendMode": "normal",
                "frequency": 0.008,
                "emitterLifetime": lifeSpan,
                "maxParticles": 200,
                "pos": {
                    "x": xPos,
                    "y": yPos
                },
                "addAtBack": false,
                "spawnType": "point"
            }
        );

        trail.autoUpdate = true;
        trail.playOnce();
        
        return trail;
    }
}

export default Fireballs;

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.