All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.almasb.fxgl.particle.ParticleEmitter Maven / Gradle / Ivy

There is a newer version: 21.1
Show newest version
/*
 * FXGL - JavaFX Game Library. The MIT License (MIT).
 * Copyright (c) AlmasB ([email protected]).
 * See LICENSE for details.
 */
package com.almasb.fxgl.particle;

import com.almasb.fxgl.core.collection.Array;
import com.almasb.fxgl.core.collection.UnorderedArray;
import com.almasb.fxgl.core.math.FXGLMath;
import com.almasb.fxgl.core.pool.Pools;
import com.almasb.fxgl.texture.Texture;
import javafx.animation.Interpolator;
import javafx.beans.property.*;
import javafx.geometry.Point2D;
import javafx.scene.effect.BlendMode;
import javafx.scene.image.Image;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.util.Duration;

import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * A general particle emitter.
 * The configuration is done via setters, which allow
 * changing how the particle is emitted and rendered.
 *
 * @author Almas Baimagambetov (AlmasB) ([email protected])
 */
public final class ParticleEmitter {

    private IntegerProperty numParticles = new SimpleIntegerProperty(25);

    public IntegerProperty numParticlesProperty() {
        return numParticles;
    }

    /**
     * @return number of particles being spawned per emission
     */
    public int getNumParticles() {
        return numParticles.get();
    }

    /**
     * @param numParticles number of particles being spawned per emission
     */
    public void setNumParticles(int numParticles) {
        this.numParticles.set(numParticles);
    }

    private DoubleProperty emissionRate = new SimpleDoubleProperty(1.0);

    public DoubleProperty emissionRateProperty() {
        return emissionRate;
    }

    /**
     * @return emission rate effective value in [0..1]
     */
    public double getEmissionRate() {
        return emissionRate.get();
    }

    /**
     * Set the emission rate. The value will be effectively
     * clamped to [0..1].
     * 
  • 1.0 - emission will occur every frame
  • *
  • 0.5 - emission will occur every 2nd frame
  • *
  • 0.33 - emission will occur every 3rd frame
  • *
  • 0.0 - emission will never occur
  • * etc. * * @param emissionRate emission rate */ public void setEmissionRate(double emissionRate) { this.emissionRate.set(emissionRate); } private int maxEmissions = Integer.MAX_VALUE; public int getMaxEmissions() { return maxEmissions; } public void setMaxEmissions(int maxEmissions) { this.maxEmissions = maxEmissions; } public boolean isFinished() { return emissions == maxEmissions; } private DoubleProperty minSize = new SimpleDoubleProperty(9.0); /** * @return minimum particle size */ public double getMinSize() { return minSize.get(); } public DoubleProperty minSizeProperty() { return minSize; } public void setMinSize(double minSize) { this.minSize.set(minSize); } private DoubleProperty maxSize = new SimpleDoubleProperty(12.0); /** * @return maximum particle size */ public double getMaxSize() { return maxSize.get(); } public DoubleProperty maxSizeProperty() { return maxSize; } public void setMaxSize(double maxSize) { this.maxSize.set(maxSize); } /** * Set size to particles. * The created size will be a random value * between min (incl) and max (excl). * * @param min minimum size * @param max maximum size */ public void setSize(double min, double max) { setMinSize(min); setMaxSize(max); } /** * @return random size between min and max size */ private double getRandomSize() { return FXGLMath.random(getMinSize(), getMaxSize()); } private ObjectProperty startColor = new SimpleObjectProperty<>(Color.TRANSPARENT); private ObjectProperty endColor = new SimpleObjectProperty<>(Color.TRANSPARENT); public Paint getStartColor() { return startColor.get(); } public ObjectProperty startColorProperty() { return startColor; } public void setStartColor(Paint startColor) { this.startColor.set(startColor); } public Paint getEndColor() { return endColor.get(); } public ObjectProperty endColorProperty() { return endColor; } public void setEndColor(Paint endColor) { this.endColor.set(endColor); } public void setColor(Paint color) { setStartColor(color); setEndColor(color); } private ObjectProperty blendMode = new SimpleObjectProperty<>(BlendMode.SRC_OVER); public BlendMode getBlendMode() { return blendMode.get(); } public ObjectProperty blendModeProperty() { return blendMode; } public void setBlendMode(BlendMode blendMode) { this.blendMode.set(blendMode); } private ObjectProperty interpolator = new SimpleObjectProperty<>(Interpolator.LINEAR); public Interpolator getInterpolator() { return interpolator.get(); } public ObjectProperty interpolatorProperty() { return interpolator; } public void setInterpolator(Interpolator interpolator) { this.interpolator.set(interpolator); } private BooleanProperty allowParticleRotation = new SimpleBooleanProperty(false); public boolean isAllowParticleRotation() { return allowParticleRotation.get(); } public BooleanProperty allowParticleRotationProperty() { return allowParticleRotation; } public void setAllowParticleRotation(boolean allowParticleRotation) { this.allowParticleRotation.set(allowParticleRotation); } /* FUNCTION CONFIGURATORS */ private Consumer control = null; public Consumer getControl() { return control; } /** * Set control function to override velocity and acceleration * of each particle. */ public void setControl(Consumer control) { this.control = control; } private Function parametricEquation = null; public Function getParametricEquation() { return parametricEquation; } public void setParametricEquation(Function parametricEquation) { this.parametricEquation = parametricEquation; } private Supplier accelerationFunction = () -> Point2D.ZERO; /** * * @return gravity function */ public Supplier getAccelerationFunction() { return accelerationFunction; } /** * Set gravity function. The supplier function is invoked * every time when a new particle is spawned. * * Adds gravitational pull to particles. Once created * the particle's movement will be biased towards * the vector. * * @param gravityFunction gravity vector supplier function * @defaultValue (0, 0) */ public void setAccelerationFunction(Supplier gravityFunction) { this.accelerationFunction = gravityFunction; } private Function velocityFunction = (i) -> Point2D.ZERO; /** * Set initial velocity function. Particles when spawned will use the function * to obtain initial velocity. * * @param velocityFunction the velocity function */ public void setVelocityFunction(Function velocityFunction) { this.velocityFunction = velocityFunction; } private Function spawnPointFunction = (i) -> Point2D.ZERO; /** * Particles will use the function to obtain spawn points. * These spawn points are local to the entity (to which this emitter is attached) coordinate system. * * @param spawnPointFunction supplier of spawn points */ public void setSpawnPointFunction(Function spawnPointFunction) { this.spawnPointFunction = spawnPointFunction; } private Function scaleOriginFunction = (i) -> Point2D.ZERO; /** * Set scale origin for each particle. */ public void setScaleOriginFunction(Function scaleOriginFunction) { this.scaleOriginFunction = scaleOriginFunction; } private Function scaleFunction = (i) -> Point2D.ZERO; /** * Scale function defines how the size of particles change over time. * * @param scaleFunction scaling function */ public void setScaleFunction(Function scaleFunction) { this.scaleFunction = scaleFunction; } private Supplier entityScaleFunction = () -> new Point2D(1, 1); public void setEntityScaleFunction(Supplier entityScaleFunction) { this.entityScaleFunction = entityScaleFunction; } private Function expireFunction = (i) -> Duration.seconds(1); /** * Expire function is used to obtain expire time for particles. * * @param expireFunction function to supply expire time */ public void setExpireFunction(Function expireFunction) { this.expireFunction = expireFunction; } private Image sourceImage = null; /** * Set source image for this emitter to produce particles. * Default: null. * * @param sourceImage the image */ public void setSourceImage(Image sourceImage) { this.sourceImage = sourceImage; } /** * Set source image from the texture for this emitter to produce particles. * * @param texture the texture whose image is used */ public void setSourceImage(Texture texture) { setSourceImage(texture.getImage()); } private boolean isPaused = false; boolean isPaused() { return isPaused; } void pause() { isPaused = true; } void resume() { isPaused = false; } /** * Emission rate accumulator. Default value 1.0 * so that when emitter starts working, it will emit in the same frame */ private double rateAC = 1.0; /** * Number of times particles have been emitted. */ private int emissions = 0; private Array emissionParticles = new UnorderedArray<>(getNumParticles()); /** * Emits {@link #numParticles} particles at x, y. This is * called every frame, however {@link #emissionRate} will * decide whether to spawn particles or not. If the emitter * is not ready, an empty list is returned. * * @param x x coordinate * @param y y coordinate * @return list of particles spawned * @implNote cached array is used, do not obtain ownership */ Array emit(double x, double y) { double rate = getEmissionRate(); rateAC += rate; if (rateAC < 1 || rate == 0 || isPaused || isFinished()) { return Array.empty(); } rateAC = 0; emissions++; emissionParticles.clear(); int num = getNumParticles(); for (int i = 0; i < num; i++) { emissionParticles.add(emit(i, x, y)); } return emissionParticles; } /** * Emits a single particle with index i. * X and Y are coordinates of the particle entity this emitter is attached to. * * @param i particle index from 0 to {@link #numParticles} * @param x top left X of the particle entity * @param y top left Y of the particle entity * @return particle */ private Particle emit(int i, double x, double y) { Particle particle = Pools.obtain(Particle.class); particle.init(getControl(), sourceImage, spawnPointFunction.apply(i).add(x, y), velocityFunction.apply(i), accelerationFunction.get(), getRandomSize(), scaleOriginFunction.apply(i), scaleFunction.apply(i), entityScaleFunction.get(), expireFunction.apply(i), getStartColor(), getEndColor(), getBlendMode(), getInterpolator(), isAllowParticleRotation(), getParametricEquation()); return particle; } }




    © 2015 - 2025 Weber Informatics LLC | Privacy Policy