Updated ParticleEmitters and Factories

This commit is contained in:
J. Neugebauer
2024-12-03 16:23:24 +01:00
parent 3196914564
commit 3ab9701629
9 changed files with 281 additions and 61 deletions

View File

@@ -191,6 +191,22 @@ public class Vector extends Point2D.Double {
return new Vector(x, y); return new Vector(x, y);
} }
public int getIntX() {
return (int)this.x;
}
public int getRoundedX() {
return (int)Math.round(this.x);
}
public int getIntY() {
return (int)this.y;
}
public int getRoundedY() {
return (int)Math.round(this.y);
}
/** /**
* Setzt die Komponenten dieses Vektors neu. * Setzt die Komponenten dieses Vektors neu.
* *

View File

@@ -14,20 +14,12 @@ public class BasicParticle extends Particle {
super(); super();
} }
public BasicParticle(Color startColor) { public BasicParticle( Color startColor ) {
this(0, startColor, null); this(startColor, null);
} }
public BasicParticle(Color startColor, Color finalColor) { public BasicParticle( Color startColor, Color finalColor ) {
this(0, startColor, finalColor); super();
}
public BasicParticle( int pLifetime ) {
super(pLifetime);
}
public BasicParticle( int pLifetime, Color startColor, Color finalColor ) {
super(pLifetime);
this.color = startColor; this.color = startColor;
this.startColor = startColor; this.startColor = startColor;
@@ -59,8 +51,8 @@ public class BasicParticle extends Particle {
} }
@Override @Override
public void spawn( Vector pPosition, Vector pVelocity ) { public void spawn( int pLifetime, Vector pPosition, Vector pVelocity ) {
super.spawn(pPosition, pVelocity); super.spawn(pLifetime, pPosition, pVelocity);
this.color = this.startColor; this.color = this.startColor;
} }
@@ -68,7 +60,7 @@ public class BasicParticle extends Particle {
public void update( double delta ) { public void update( double delta ) {
super.update(delta); super.update(delta);
if( startColor != null && finalColor != null ) { if( isActive() && startColor != null && finalColor != null ) {
double t = 1.0 - lifetime / maxLifetime; double t = 1.0 - lifetime / maxLifetime;
this.color = Color.interpolate(startColor, finalColor, t); this.color = Color.interpolate(startColor, finalColor, t);
} }
@@ -76,9 +68,9 @@ public class BasicParticle extends Particle {
@Override @Override
public void draw( Graphics2D graphics ) { public void draw( Graphics2D graphics ) {
if( this.color != null ) { if( isActive() && this.color != null ) {
graphics.setColor(this.color.getJavaColor()); graphics.setColor(this.color.getJavaColor());
graphics.fillOval((int) position.x, (int) position.y, 6, 6); graphics.fillOval(position.getIntX() - 3, position.getIntY() - 3, 6, 6);
} }
} }

View File

@@ -4,16 +4,18 @@ import schule.ngb.zm.Color;
public class BasicParticleFactory implements ParticleFactory { public class BasicParticleFactory implements ParticleFactory {
private final Color startColor; private final Color startColor;
private final Color finalColor; private final Color finalColor;
private final int maxLifetime = 50; private boolean fadeOut = true;
public BasicParticleFactory() { public BasicParticleFactory() {
this.startColor = new Color(128, 128, 129); this(null, null);
this.finalColor = new Color(128, 128, 129, 0); }
public BasicParticleFactory( Color startColor ) {
this(startColor, null);
} }
public BasicParticleFactory( Color startColor, Color finalColor ) { public BasicParticleFactory( Color startColor, Color finalColor ) {
@@ -21,13 +23,21 @@ public class BasicParticleFactory implements ParticleFactory {
this.finalColor = finalColor; this.finalColor = finalColor;
} }
public int getMaxLifetime() { public void setFadeOut( boolean pFadeOut ) {
return maxLifetime; this.fadeOut = pFadeOut;
} }
@Override @Override
public Particle createParticle() { public Particle createParticle() {
return new BasicParticle(maxLifetime, startColor, finalColor); Color finalClr = finalColor;
if( fadeOut ) {
if( finalColor != null ) {
finalClr = new Color(finalColor, 0);
} else if( startColor != null ) {
finalClr = new Color(startColor, 0);
}
}
return new BasicParticle(startColor, finalClr);
} }
} }

View File

@@ -0,0 +1,49 @@
package schule.ngb.zm.particles;
import schule.ngb.zm.util.Log;
import java.util.Arrays;
import java.util.function.Supplier;
public class GenericParticleFactory<T extends Particle> implements ParticleFactory {
private final Class<T> type;
private final Supplier<T> supplier;
public GenericParticleFactory( Class<T> type, Object... params ) {
this.type = type;
// Create paramTypes array once
Class<?>[] paramTypes = new Class<?>[params.length];
for( int i = 0; i < params.length; i++ ) {
paramTypes[i] = params[i].getClass();
}
this.supplier = () -> {
T p = null;
try {
p = GenericParticleFactory.this.type.getDeclaredConstructor(paramTypes).newInstance(params);
} catch( Exception ex ) {
LOG.error( ex,
"Unable to create new Particle of type %s",
GenericParticleFactory.this.type.getCanonicalName()
);
}
return p;
};
}
public GenericParticleFactory( Supplier<T> supplier ) {
this.supplier = supplier;
this.type = (Class<T>)supplier.get().getClass();
}
@Override
public Particle createParticle() {
return this.supplier.get();
}
private static final Log LOG = Log.getLogger(GenericParticleFactory.class);
}

View File

@@ -7,25 +7,19 @@ import schule.ngb.zm.Vector;
public abstract class Particle extends PhysicsObject implements Updatable, Drawable { public abstract class Particle extends PhysicsObject implements Updatable, Drawable {
protected double maxLifetime = 0; protected double maxLifetime = 0, lifetime = 0;
protected double lifetime = 0;
public Particle() { public Particle() {
super(); super();
} }
public Particle( int pLifetime ) { public void spawn( int pLifetime, Vector pPosition, Vector pVelocity ) {
super(); this.maxLifetime = pLifetime;
maxLifetime = pLifetime; this.lifetime = pLifetime;
} this.position = pPosition.copy();
this.velocity = pVelocity.copy();
public void spawn( Vector pPosition, Vector pVelocity ) { this.acceleration = new Vector();
lifetime = maxLifetime;
position = pPosition.copy();
velocity = pVelocity.copy();
acceleration = new Vector();
} }
@Override @Override
@@ -59,6 +53,8 @@ public abstract class Particle extends PhysicsObject implements Updatable, Drawa
super.update(delta); super.update(delta);
// lifetime -= delta; // lifetime -= delta;
lifetime -= 1; lifetime -= 1;
// TODO: (ngb) calculate delta based on lifetime?
} }
} }

View File

@@ -25,20 +25,23 @@ public class ParticleEmitter implements Updatable, Drawable {
public Vector direction = new Vector(); public Vector direction = new Vector();
public double strength = 100.0;
public int angle = 0; public int angle = 0;
public double randomness = 0.0; public double randomness = 0.0;
// private Vortex vortex = null; // private Vortex vortex = null;
public ParticleEmitter( double pX, double pY, int pParticlesPerFrame, ParticleFactory pFactory ) { public ParticleEmitter( double pX, double pY, int pParticleLifetime, int pParticlesPerFrame, ParticleFactory pFactory ) {
this.position = new Vector(pX, pY); this.position = new Vector(pX, pY);
this.particlesPerFrame = pParticlesPerFrame; this.particlesPerFrame = pParticlesPerFrame;
this.particleLifetime = pParticleLifetime;
this.particleFactory = pFactory; this.particleFactory = pFactory;
// Create particle pool // Create particle pool
this.particles = new Particle[particlesPerFrame * pFactory.getMaxLifetime()]; this.particles = new Particle[particlesPerFrame * pParticleLifetime];
this.direction = Vector.random(8, 16).setLength(100); this.direction = Vector.random(8, 16).normalize();
// vortex = new Vortex(position.copy().add(-10, -10), -.2, 8); // vortex = new Vortex(position.copy().add(-10, -10), -.2, 8);
} }
@@ -54,6 +57,8 @@ public class ParticleEmitter implements Updatable, Drawable {
} }
public void start() { public void start() {
this.direction.normalize();
// Partikel initialisieren // Partikel initialisieren
for( int i = 0; i < particles.length; i++ ) { for( int i = 0; i < particles.length; i++ ) {
particles[i] = particleFactory.createParticle(); particles[i] = particleFactory.createParticle();
@@ -84,16 +89,13 @@ public class ParticleEmitter implements Updatable, Drawable {
int ppf = particlesPerFrame; int ppf = particlesPerFrame;
Particle nextParticle = getNextParticle(); Particle nextParticle = getNextParticle();
while( ppf > 0 && nextParticle != null ) { while( ppf > 0 && nextParticle != null ) {
// TODO: (ngb) randomize lifetime of particles in Factory? int lifetime = (int) random(particleLifetime);
// int pLeben = (int) random(particleLifetime);
// p.lifetime = pLeben;
// p.maxLifetime = pLeben;
double rotation = (angle / 2.0) - (int) (Math.random() * angle); double rotation = (angle / 2.0) - (int) (Math.random() * angle);
Vector velocity = direction.copy().rotate(rotation); Vector velocity = direction.copy().scale(strength).rotate(rotation);
velocity.scale(random()); velocity.scale(random());
nextParticle.spawn(this.position, velocity); nextParticle.spawn(lifetime, this.position, velocity);
nextParticle = getNextParticle(); nextParticle = getNextParticle();
ppf -= 1; ppf -= 1;
} }
@@ -130,7 +132,7 @@ public class ParticleEmitter implements Updatable, Drawable {
public void draw( Graphics2D graphics ) { public void draw( Graphics2D graphics ) {
java.awt.Color current = graphics.getColor(); java.awt.Color current = graphics.getColor();
for( Particle particle : particles ) { for( Particle particle : particles ) {
if( particle != null ) { if( particle != null && particle.isVisible() ) {
particle.draw(graphics); particle.draw(graphics);
} }
} }

View File

@@ -4,6 +4,4 @@ public interface ParticleFactory {
Particle createParticle(); Particle createParticle();
int getMaxLifetime();
} }

View File

@@ -0,0 +1,35 @@
package schule.ngb.zm.particles;
import schule.ngb.zm.Color;
import java.awt.Graphics2D;
public class StarParticle extends BasicParticle {
public StarParticle() {
super();
this.startColor = Color.PURE_GREEN;
}
public StarParticle( Color startColor ) {
this(startColor, null);
}
public StarParticle( Color startColor, Color finalColor ) {
super();
this.color = startColor;
this.startColor = startColor;
this.finalColor = finalColor;
}
@Override
public void draw( Graphics2D graphics ) {
if( isActive() && this.color != null ) {
graphics.setColor(this.color.getJavaColor());
graphics.drawLine((int) position.x - 3, (int) position.y - 3, (int) position.x + 3, (int) position.y + 3);
graphics.drawLine((int) position.x + 3, (int) position.y - 3, (int) position.x - 3, (int) position.y + 3);
}
}
}

View File

@@ -1,36 +1,158 @@
package schule.ngb.zm.particles; package schule.ngb.zm.particles;
import schule.ngb.zm.Zeichenmaschine; import schule.ngb.zm.*;
import schule.ngb.zm.layers.DrawableLayer; import schule.ngb.zm.layers.DrawableLayer;
import schule.ngb.zm.layers.DrawingLayer;
public class ParticleExample extends Zeichenmaschine { import java.awt.Graphics2D;
public class ParticleExample extends Testmaschine {
public static void main( String[] args ) { public static void main( String[] args ) {
new ParticleExample(); new ParticleExample();
} }
public ParticleExample() { public ParticleExample() {
super(400, 400, "ZM: Particles"); super();
} }
ParticleEmitter pgen; ParticleEmitter pe1, pe2, pe3;
Rocket r;
public void setup() { public void setup() {
pgen = new ParticleEmitter( getLayer(DrawingLayer.class).hide();
200, 200, 1, background.setColor(0);
new BasicParticleFactory() drawing.noStroke();
drawing.setFillColor(WHITE);
for( int i = 0; i < 1000; i++ ) {
drawing.point(random(0, canvasWidth), random(0, canvasHeight));
}
pe1 = new ParticleEmitter(
100, 100, 50, 5,
// new BasicParticleFactory(PINK, BLUE)
new GenericParticleFactory<Particle>(() -> {
return new Particle() {
@Override
public void draw( Graphics2D graphics ) {
graphics.setColor(Color.MAGENTA.getJavaColor());
graphics.rotate(Constants.radians(45), (int) position.x, (int) position.y);
graphics.drawRect((int) position.x - 3, (int) position.y - 3, 6, 6);
graphics.rotate(Constants.radians(-45), (int) position.x, (int) position.y);
}
};
})
); );
pgen.randomness = .5; pe1.randomness = .2;
pgen.angle = 45; pe1.angle = 45;
pe1.strength = 200;
pe2 = new ParticleEmitter(
300, 300, 50, 10,
new GenericParticleFactory(() -> new StarParticle(RED, new Color(BLUE, 55)))
//new GenericParticleFactory(StarParticle.class, RED, new Color(BLUE, 55))
);
pe2.direction = NORTH.asVector().scale(100);
pe2.randomness = .8;
pe2.angle = 90;
pe2.strength = 200;
pe3 = new ParticleEmitter(
100, 400, 20, 8,
new BasicParticleFactory(YELLOW, RED)
);
pe3.direction = SOUTH.asVector();
pe3.randomness = .33;
pe3.angle = 30;
DrawableLayer drawables = new DrawableLayer(); DrawableLayer drawables = new DrawableLayer();
addLayer(drawables); addLayer(drawables);
drawables.add(pgen); drawables.add(pe1, pe2, pe3);
pgen.start();
pe1.start();
pe2.start();
pe3.start();
r = new Rocket(200, 400);
drawables.add(r);
r.start();
} }
@Override @Override
public void update( double delta ) { public void update( double delta ) {
pgen.update(delta); pe1.update(delta);
pe2.update(delta);
pe3.update(delta);
pe3.position.add(NORTH.asVector().scale(Constants.map(runtime, 0, 1000, 0, 10) * delta));
if( r.isActive() ) {
r.update(delta);
}
}
class Rocket extends PhysicsObject implements Drawable {
ParticleEmitter trail;
private boolean starting = false;
private double acc = 4;
public Rocket( double x, double y ) {
super(new Vector(x, y));
trail = new ParticleEmitter(
x, y, 30, 6,
new BasicParticleFactory(YELLOW, RED)
);
trail.direction = SOUTH.asVector();
trail.randomness = .33;
trail.angle = 30;
trail.position = this.position;
}
public void start() {
starting = true;
trail.start();
}
@Override
public boolean isActive() {
return starting;
}
@Override
public boolean isVisible() {
return true;
}
@Override
public void update( double delta ) {
super.update(delta);
if( this.acceleration.lengthSq() < acc * acc ) {
this.accelerate(NORTHWEST.asVector().scale(acc));
}
trail.update(delta);
}
@Override
public void draw( Graphics2D graphics ) {
graphics.rotate(-Constants.radians(velocity.angle() + 180), position.getIntX(), position.getIntY());
trail.draw(graphics);
graphics.setColor(WHITE.getJavaColor());
graphics.fillRect(position.getIntX() - 6, position.getIntY() - 32, 12, 32);
graphics.fillPolygon(
new int[]{position.getIntX() - 6, position.getIntX(), position.getIntX() + 6},
new int[]{position.getIntY() - 32, position.getIntY() - 40, position.getIntY() - 32},
3
);
graphics.rotate(Constants.radians(velocity.angle() + 180), position.getIntX(), position.getIntY());
}
} }
} }