game_actor_particle_particle_emitter.js

import Vec2 from "../../vector/vec2.js";
import Vec2Gradient from "../../../util/gradient/vec2_gradient.js";
import Actor from "../actor.js";
import Particle from "./particle.js";
import NumberGradient from "../../../util/gradient/number_gradient.js";

/**
 * @module ParticleEmitter
 * @fileoverview Contains ParticleEmitter class.
 */

/**
 * @class
 * @extends Actor
 * Emits particles.
 */
class ParticleEmitter extends Actor {
    /**
     * Create a new particle emitter.
     * @param {Vec2} pos Position of emitter
     * @param {number} rate Seconds between spawning particles, or number to spawn (if continuous is false)
     * @param {boolean} continuous Whether particles should be spawned repeatedly every [rate] seconds, or all at once
     * @param {number} lifespan Lifespan of particles
     * @param {string|Sprite} sprite Sprite or identifier of sprite to use for particles
     * @param {"random"|number} rotation Rotation of particles
     * @param {number} spin Angular velocity of particles
     * @param {number|Vec2|Vec2Gradient} scale Scale of particles
     * @param {number|NumberGradient} opacity Opacity of particles
     * @param {number|Vec2} range Range of spawning particles
     * @param {number|Vec2|Vec2Gradient} velocity Velocity of particles (if a number is given, it will be outward from
     * the source)
     * @param {Object} events Events to add to emitter
     * @constructor
     */
    constructor({
        pos = new Vec2(),

        rate = 0.5,
        continuous = true,

        lifespan = 1,

        sprite = null,

        rotation = "random",
        spin = 0,
        scale = 1,

        opacity = 1,

        range = 0,

        velocity = new Vec2(),

        events = {}
    } = {}) {
        super({
            pos: pos,
            events: events
        });

        this.rate = rate;
        this.continuous = continuous;

        this.lifespan = lifespan;

        this.sprite = sprite;

        this.pRot = rotation;
        this.spin = spin;
        this.pScale = new Vec2Gradient({0: new Vec2(1, 1)});
        if (typeof scale === "number" || scale instanceof Vec2)
            this.pScale = new Vec2Gradient({0: scale});
        else if (scale instanceof Vec2Gradient)
            this.pScale = scale;

        this.opacity = new NumberGradient({0: 1});
        if (typeof opacity === "number")
            this.opacity = new NumberGradient({0: opacity});
        else if (opacity instanceof NumberGradient)
            this.opacity = opacity;

        this.range = 0;
        if (typeof range === "number")
            this.range = new Vec2(range, range);
        else if (range instanceof Vec2)
            this.range = range.copy();

        this.velocity = new Vec2Gradient();
        if (typeof velocity === "number")
            this.velocity = velocity;
        else if (velocity instanceof Vec2)
            this.velocity = new Vec2Gradient({0: velocity});
        else if (velocity instanceof Vec2Gradient)
            this.velocity = velocity;


        // Timer

        this._timer = 0;
    }

    init() {
        super.init();

        if (this.continuous)
            return;

        this._spawnParticles(this.rate);

        this.delete();
    }

    update(elapsed) {
        super.update(elapsed);

        if (!this.continuous)
            return;

        this._timer += elapsed;

        if (this._timer >= this.rate) {
            this._spawnParticle();

            this._timer = 0;
        }
    }


    //
    // Spawning Particles
    //

    /**
     * Spawn a new particle.
     * @private
     */
    _spawnParticle() {
        const diff = new Vec2(
            this.range.x * (Math.random() * 2 - 1),
            this.range.y * (Math.random() * 2 - 1)
        );

        let vel = this.velocity;
        if (typeof vel === "number")
            vel = new Vec2Gradient({
                0: diff.norm().mulv(this.velocity)
            });

        const particle = this.space.addActor(new Particle({
            pos: this.globalPos.add(diff),

            lifespan: this.lifespan,

            sprite: this.sprite,
            rotation: this.pRot,
            spin: this.spin,
            scale: this.pScale,

            opacity: this.opacity,

            velocity: vel
        }));

        this.doEvent("spawnParticle", particle);
    }

    /**
     * Spawn multiple new particles.
     * @param {number} amount Number of new particles to spawn
     * @private
     */
    _spawnParticles(amount) {
        for (let i = 0; i < amount; i++)
            this._spawnParticle();
    }
}

export default ParticleEmitter;