pixi.jsparticle-systemspine

How to get Pixi Particles to behave properly when set as a child of a slot inside of a spine animation


protected createParticleAnimation ( data: IAnticipationParticle ): particles.Emitter {
    let particleResource = PIXI.loader.resources[ data.name ].data;
    let particleImages: Array<Texture> = new Array<Texture>();

    let container = new particles.ParticleContainer();
    container.scale.set( 1, -1 );
    let spineAnim = this.anticipationAnimations[ 0 ] as spine.Spine;

    spineAnim.slotContainers.forEach( ( slotContainer: Container ) => {
        if ( slotContainer.name == 'CoinParticles' ) {
            container.position.set( slotContainer.children[ 0 ].x * 2, slotContainer.children[ 0 ].y * 2 );
            slotContainer.addChild( container );
            return;
        }
    } );

    data.images.forEach( ( image: string ) => {
        particleImages.push( PIXI.Texture.fromImage( image ) );
    } );

    let animation = new PIXI.particles.Emitter(
        container,
        particleImages,
        particleResource
    );
    animation.emit = false;
    animation.autoUpdate = true;

    return animation;
}

So I create my spine animation in another function then create my particle effect as shown above and attach it to a slotContainer inside of my spine animation. But when my spine animation plays the particles always follow the parents position and do not keep their world coordinates.

I believe this is because of spine but does anyone have any ideas on how to get around this?

So for example when the emitter moves the particles that have already been generate follow the x position of the emitter


Solution

  • So I ended up needing to override the updateTransform method of the pixi container.

    I passed the emitter to the container so it could update its transform. I needed to calculate the original point of the sprite when I did this. This needs to be called after the emitter is created because the container needs to passed into the creation of the emitter.

    Then you need to move the containers transform back to where it was originally so the children ( the particles ) can behave properly. You then move the emitters spawn position along with the parent.

    import { particles, Point } from 'pixi.js';
    
    /**
     * A container to be used when attaching a particle emitter to a spine animation
     */
    export class SpineParticleContainer extends particles.ParticleContainer {
    
        /**The emitter that is drawning to the container */
        protected emitter: particles.Emitter;
    
        /**The original position of the sprite when the emitter is set */
        protected origin: Point;
    
        constructor () {
            super();
        }
    
        /**
         * Sets the containers emittter so it can update the emitters position
         * @param emiter The particle emitter to pass in, this should be the particle emitter assigned to this container already
         */
        public setEmitter ( emiter: particles.Emitter ): void {
            this.emitter = emiter;
            this.origin = new Point( this.parent.worldTransform.tx, this.parent.worldTransform.ty );
        }
    
        /**Override update transform to reposition the container at its origin position and move the emitter along with the animation */
        public updateTransform () {
            this._boundsID++;
            this.worldAlpha = this.alpha * this.parent.worldAlpha;
    
            let position = new Point( this.parent.worldTransform.tx, this.parent.worldTransform.ty );
            let newPosition = new Point( this.origin.x - position.x, this.origin.y - position.y );
            this.position.set( newPosition.x, newPosition.y );
            this.transform.updateTransform( this.parent.transform );
            for ( let i = 0, j = this.children.length; i < j; i++ ) {
                const child = this.children[ i ];
    
                if ( child.visible ) {
                    child.updateTransform();
                }
            }
    
            this.emitter.spawnPos.set( this.parent.worldTransform.tx, this.parent.worldTransform.ty );
        }
    }