Ok, here are some examples.
Particle System Simplification
public interface Particle extends Entity {
public void reset( float lifetime );
public Vec2f location();
public Vec2f velocity();
public Vec2f acceleration();
public Vec2f size();
public Vec2f scale();
public Vec2f scaleVelocity();
public Vec2f scaleAcceleration();
public Scalarf angle();
public Scalarf angleVelocity();
public Scalarf angleAcceleration();
public Color color();
public Tile tile();
public void tile( Tile tile );
public float lifetime();
public float age();
}
And here’s an example of something I call a ParticleInfluence. It’s something that gets called every frame for every particle in the system which has this influence.
public class InfluenceAngle extends InfluencePath<Scalarf> {
public InfluenceAngle( Path<Scalarf> path ) {
super( path );
}
protected void onInfluence( Particle p, float elapsed ) {
path.set( p.angle(), p.age() / p.lifetime() ); // path determines what the angle is depending on a delta [0, 1]
}
}
As you see this one is for the angle. I ALSO have one for color, acceleration, scale, size, tile, and velocity. Now If I used components, it would look like this:
public class InfluencePath<T> extends ParticleInfluence {
public Path<T> path;
public Component<T> component;
public InfluencePath( Component<T> component, Path<T> path ) {
...
}
protected void onInfluence( Particle p, float elapsed ) {
path.set( p.get( component ), p.age() / p.lifetime() );
}
}
So instead of doing…
ParticleEmitter emitter = new ParticleEmitter();
emitter.addInfluence( new InfluenceAngle( new Tween<Scalarf>( Scalarf.ONE, new Scalarf( 2.0f ) ) );
emitter.addInfluence( new InfluenceScale( new Tween<Vec2f>( Vec2f.ONE, new Vec2f( 1.4f ) ) );
emitter.addInfluence( new InfluenceVelocity( new Tween<Vec2f>( Vec2f.ZERO, new Vec2f( 200f ) ) );
It would be:
ParticleEmitter emitter = new ParticleEmitter();
emitter.addInfluence( new InfluencePath( Particle.ANGLE, new Tween<Scalarf>( Scalarf.ONE, new Scalarf( 2.0f ) ) );
emitter.addInfluence( new InfluencePath( Particle.SCALE, new Tween<Vec2f>( Vec2f.ONE, new Vec2f( 1.4f ) ) );
emitter.addInfluence( new InfluencePath( Particle.VELOCITY, new Tween<Vec2f>( Vec2f.ZERO, new Vec2f( 200f ) ) );
And I could also add:
public class InfluenceAdding<T> extends ParticleInfluence {
public Component<T> subject;
public Component<T> addend;
protected void onInfluence( Particle p, float elapsed ) {
p.get( subject ).add( p.get( addend ), elapsed );
}
}
So I can do things like:
emitter.addInfluence( new InfluenceAdding<Vec2f>( Particle.VELOCITY, Particle.ACCELERATION ) );
emitter.addInfluence( new InfluenceAdding<Vec2f>( Particle.POSITION, Particle.VELOCITY ) );
emitter.addInfluence( new InfluenceAdding<Scalarf>( Particle.ANGLE, Particle.ANGULAR_VELOCITY) );
Which stuff like this enables the user to add their own update logic, instead of having this fixed in the ParticleSystem code:
Particle p = ...
if (p.velocity() != null) {
if (p.acceleration() != null) {
p.velocity().add( p.acceleration(), elapsed );
}
p.position().add( p.velocity(), elapsed);
}
// do the same for angle and size velocity & acceleration
I hope it’s clearer now.