package loon.particle; import loon.LTexture; import loon.LTextures; import loon.canvas.LColor; import loon.geom.Vector2f; import loon.utils.MathUtils; import loon.utils.StringUtils; import loon.utils.TArray; public class SimpleConfigurableEmitter implements SimpleEmitter { private static String relativePath = ""; public static void setRelativePath(String path) { if (!path.endsWith("/")) { path += "/"; } relativePath = path; } public Range spawnInterval = new Range(100, 100); public Range spawnCount = new Range(5, 5); public Range initialLife = new Range(1000, 1000); public Range initialSize = new Range(10, 10); public Range xOffset = new Range(0, 0); public Range yOffset = new Range(0, 0); public RandomValue spread = new RandomValue(360); public SimpleValue angularOffset = new SimpleValue(0); public Range initialDistance = new Range(0, 0); public Range speed = new Range(50, 50); public SimpleValue growthFactor = new SimpleValue(0); public SimpleValue gravityFactor = new SimpleValue(0); public SimpleValue windFactor = new SimpleValue(0); public Range length = new Range(1000, 1000); public TArray<ColorRecord> colors = new TArray<ColorRecord>(); public SimpleValue startAlpha = new SimpleValue(255); public SimpleValue endAlpha = new SimpleValue(0); public LinearInterpolator alpha; public LinearInterpolator size; public LinearInterpolator velocity; public LinearInterpolator scaleY; public Range emitCount = new Range(1000, 1000); public int usePoints = SimpleParticle.INHERIT_POINTS; public boolean useOriented = false; public boolean useAdditive = false; public String name; public String imageName = ""; private LTexture image; private boolean updateImage; private boolean enabled = true; private float x; private float y; private int nextSpawn = 0; private int timeout; private int particleCount; private SimpleParticleSystem engine; private int leftToEmit; protected boolean wrapUp = false; protected boolean completed = false; protected boolean adjust; protected float adjustx; protected float adjusty; public SimpleConfigurableEmitter(String name) { this.name = name; leftToEmit = (int) emitCount.random(); timeout = (int) (length.random()); colors.add(new ColorRecord(0, LColor.white)); colors.add(new ColorRecord(1, LColor.red)); TArray<Vector2f> curve = new TArray<Vector2f>(); curve.add(new Vector2f(0.0f, 0.0f)); curve.add(new Vector2f(1.0f, 255.0f)); alpha = new LinearInterpolator(curve, 0, 255); curve = new TArray<Vector2f>(); curve.add(new Vector2f(0.0f, 0.0f)); curve.add(new Vector2f(1.0f, 255.0f)); size = new LinearInterpolator(curve, 0, 255); curve = new TArray<Vector2f>(); curve.add(new Vector2f(0.0f, 0.0f)); curve.add(new Vector2f(1.0f, 1.0f)); velocity = new LinearInterpolator(curve, 0, 1); curve = new TArray<Vector2f>(); curve.add(new Vector2f(0.0f, 0.0f)); curve.add(new Vector2f(1.0f, 1.0f)); scaleY = new LinearInterpolator(curve, 0, 1); } public void setImageName(String imageName) { if (StringUtils.isEmpty(imageName)) { imageName = null; } this.imageName = imageName; if (imageName == null) { image = null; } else { updateImage = true; } } public String getImageName() { return imageName; } public void setPosition(float x, float y) { setPosition(x, y, true); } public void setPosition(float x, float y, boolean moveParticles) { if (moveParticles) { adjust = true; adjustx -= this.x - x; adjusty -= this.y - y; } this.x = x; this.y = y; } public float getX() { return x; } public float getY() { return y; } @Override public boolean isEnabled() { return enabled; } @Override public void setEnabled(boolean enabled) { this.enabled = enabled; } @Override public void update(SimpleParticleSystem system, long delta) { this.engine = system; if (!adjust) { adjustx = 0; adjusty = 0; } else { adjust = false; } if (updateImage) { updateImage = false; image = LTextures.loadTexture(relativePath + imageName); } if ((wrapUp) || ((length.isEnabled()) && (timeout < 0)) || ((emitCount.isEnabled() && (leftToEmit <= 0)))) { if (particleCount == 0) { completed = true; } } particleCount = 0; if (wrapUp) { return; } if (length.isEnabled()) { if (timeout < 0) { return; } timeout -= delta; } if (emitCount.isEnabled()) { if (leftToEmit <= 0) { return; } } nextSpawn -= delta; if (nextSpawn < 0) { nextSpawn = (int) spawnInterval.random(); int count = (int) spawnCount.random(); for (int i = 0; i < count; i++) { SimpleParticle p = system.getNewParticle(this, initialLife.random()); p.setSize(initialSize.random()); p.setPosition(x + xOffset.random(), y + yOffset.random()); p.setVelocity(0, 0, 0); float dist = initialDistance.random(); float power = speed.random(); if ((dist != 0) || (power != 0)) { float s = spread.getValue(0); float ang = (s + angularOffset.getValue(0) - (spread .getValue() / 2)) - 90; float xa = (MathUtils.cos(MathUtils.toDegrees(ang)) * dist); float ya = (MathUtils.sin(MathUtils.toDegrees(ang)) * dist); p.adjustPosition(xa, ya); float xv = MathUtils.cos(MathUtils.toDegrees(ang)); float yv = MathUtils.sin(MathUtils.toDegrees(ang)); p.setVelocity(xv, yv, power * 0.001f); } if (image != null) { p.setImage(image); } ColorRecord start = (ColorRecord) colors.get(0); p.setColor(start.col.r, start.col.g, start.col.b, startAlpha.getValue(0) / 255.0f); p.setUsePoint(usePoints); p.setOriented(useOriented); if (emitCount.isEnabled()) { leftToEmit--; if (leftToEmit <= 0) { break; } } } } } @Override public void updateParticle(SimpleParticle particle, long delta) { particleCount++; particle.x += adjustx; particle.y += adjusty; particle.adjustVelocity(windFactor.getValue(0) * 0.00005f * delta, gravityFactor.getValue(0) * 0.00005f * delta); float offset = particle.getLife() / particle.getOriginalLife(); float inv = 1 - offset; float colOffset = 0; float colInv = 1; LColor startColor = null; LColor endColor = null; for (int i = 0; i < colors.size - 1; i++) { ColorRecord rec1 = (ColorRecord) colors.get(i); ColorRecord rec2 = (ColorRecord) colors.get(i + 1); if ((inv >= rec1.pos) && (inv <= rec2.pos)) { startColor = rec1.col; endColor = rec2.col; float step = rec2.pos - rec1.pos; colOffset = inv - rec1.pos; colOffset /= step; colOffset = 1 - colOffset; colInv = 1 - colOffset; } } if (startColor != null) { float r = (startColor.r * colOffset) + (endColor.r * colInv); float g = (startColor.g * colOffset) + (endColor.g * colInv); float b = (startColor.b * colOffset) + (endColor.b * colInv); float a; if (alpha.isActive()) { a = alpha.getValue(inv) / 255.0f; } else { a = ((startAlpha.getValue(0) / 255.0f) * offset) + ((endAlpha.getValue(0) / 255.0f) * inv); } particle.setColor(r, g, b, a); } if (size.isActive()) { float s = size.getValue(inv); particle.setSize(s); } else { particle.adjustSize(delta * growthFactor.getValue(0) * 0.001f); } if (velocity.isActive()) { particle.setSpeed(velocity.getValue(inv)); } if (scaleY.isActive()) { particle.setScaleY(scaleY.getValue(inv)); } } @Override public boolean completed() { if (engine == null) { return false; } if (length.isEnabled()) { if (timeout > 0) { return false; } return completed; } if (emitCount.isEnabled()) { if (leftToEmit > 0) { return false; } return completed; } if (wrapUp) { return completed; } return false; } public void replay() { reset(); nextSpawn = 0; leftToEmit = (int) emitCount.random(); timeout = (int) (length.random()); } public void reset() { completed = false; if (engine != null) { engine.releaseAll(this); } } public void replayCheck() { if (completed()) { if (engine != null) { if (engine.getParticleCount() == 0) { replay(); } } } } public interface Value { public float getValue(float time); } public class SimpleValue implements Value { private float value; private SimpleValue(float value) { this.value = value; } @Override public float getValue(float time) { return value; } public void setValue(float value) { this.value = value; } } public class RandomValue implements Value { private float value; private RandomValue(float value) { this.value = value; } public float getValue(float time) { return (MathUtils.random() * value); } public void setValue(float value) { this.value = value; } public float getValue() { return value; } } public class LinearInterpolator implements Value { private TArray<Vector2f> curve; private boolean active; private int min; private int max; public LinearInterpolator(TArray<Vector2f> curve, int min, int max) { this.curve = curve; this.min = min; this.max = max; this.active = false; } public void setCurve(TArray<Vector2f> curve) { this.curve = curve; } public TArray<Vector2f> getCurve() { return curve; } @Override public float getValue(float t) { Vector2f p0 = (Vector2f) curve.get(0); for (int i = 1; i < curve.size; i++) { Vector2f p1 = (Vector2f) curve.get(i); if (t >= p0.getX() && t <= p1.getX()) { float st = (t - p0.getX()) / (p1.getX() - p0.getX()); float r = p0.getY() + st * (p1.getY() - p0.getY()); return r; } p0 = p1; } return 0; } public boolean isActive() { return active; } public void setActive(boolean active) { this.active = active; } public int getMax() { return max; } public void setMax(int max) { this.max = max; } public int getMin() { return min; } public void setMin(int min) { this.min = min; } } public class ColorRecord { public float pos; public LColor col; public ColorRecord(float pos, LColor col) { this.pos = pos; this.col = col; } } public void addColorPoint(float pos, LColor col) { colors.add(new ColorRecord(pos, col)); } public class Range { private float max; private float min; private boolean enabled = false; private Range(float min, float max) { this.min = min; this.max = max; } public float random() { return (min + (MathUtils.random() * (max - min))); } public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public float getMax() { return max; } public void setMax(float max) { this.max = max; } public float getMin() { return min; } public void setMin(float min) { this.min = min; } } @Override public boolean useAdditive() { return useAdditive; } @Override public boolean isOriented() { return this.useOriented; } @Override public boolean usePoints(SimpleParticleSystem system) { return (this.usePoints == SimpleParticle.INHERIT_POINTS) && (system.usePoints()) || (this.usePoints == SimpleParticle.USE_POINTS); } @Override public LTexture getImage() { return image; } @Override public void up() { wrapUp = true; } public void resetState() { wrapUp = false; replay(); } @Override public String toString() { return "[" + name + "]"; } }