}\r
\r
/**\r
- * Sets the size and position where the sprite will be drawn, before scaling and rotation are applied. If origin, rotation, or\r
- * scale are changed, it is slightly more efficient to set the bounds afterward.\r
+ * Sets the size and position of the sprite when drawn, before scaling and rotation are applied. If origin, rotation, or scale\r
+ * are changed, it is slightly more efficient to set the bounds afterward.\r
*/\r
public void setBounds (float x, float y, float width, float height) {\r
this.x = x;\r
}\r
\r
/**\r
+ * Sets the size of the sprite when drawn, before scaling and rotation are applied. If origin, rotation, or scale are changed,\r
+ * it is slightly more efficient to set the size afterward. If both position and size are to be changed, it is better to use\r
+ * {@link #setBounds(float, float, float, float)}.\r
+ */\r
+ public void setSize (float width, float height) {\r
+ this.width = width;\r
+ this.height = height;\r
+\r
+ if (dirty) return;\r
+\r
+ float x2 = x + width;\r
+ float y2 = y + height;\r
+ float[] vertices = this.vertices;\r
+ vertices[X1] = x;\r
+ vertices[Y1] = y;\r
+\r
+ vertices[X2] = x;\r
+ vertices[Y2] = y2;\r
+\r
+ vertices[X3] = x2;\r
+ vertices[Y3] = y2;\r
+\r
+ vertices[X4] = x2;\r
+ vertices[Y4] = y;\r
+\r
+ if (rotation != 0 || scaleX != 1 || scaleY != 1) dirty = true;\r
+ }\r
+\r
+ /**\r
* Sets the position where the sprite will be drawn. If origin, rotation, or scale are changed, it is slightly more efficient\r
- * to set the position afterward.\r
+ * to set the position afterward. If both position and size are to be changed, it is better to use\r
+ * {@link #setBounds(float, float, float, float)}.\r
*/\r
public void setPosition (float x, float y) {\r
translate(x - this.x, y - this.y);\r
}\r
\r
public void setColor (Color tint) {\r
- this.color = tint;\r
float color = tint.toFloatBits();\r
float[] vertices = this.vertices;\r
vertices[C1] = color;\r
}\r
\r
public void setColor (float r, float g, float b, float a) {\r
- color.r = r;\r
- color.g = g;\r
- color.b = b;\r
- color.a = a;\r
int intBits = ((int)(255 * a) << 24) | //\r
((int)(255 * b) << 16) | //\r
((int)(255 * g) << 8) | //\r
return scaleY;\r
}\r
\r
+ /**\r
+ * Returns the color for this sprite. Changing the returned color will have no affect. {@link #setColor(Color)} or\r
+ * {@link #setColor(float, float, float, float)} must be used.\r
+ */\r
public Color getColor () {\r
+ int intBits = Float.floatToIntBits(vertices[C1]);\r
+ Color color = this.color;\r
+ color.r = (intBits & 0xff) / 255f;\r
+ color.g = (intBits >> 8 & 0xff) / 255f;\r
+ color.b = (intBits >> 16 & 0xff) / 255f;\r
+ color.a = (intBits >> 24) / 255f;\r
return color;\r
}\r
\r
import java.io.BufferedReader;\r
import java.io.IOException;\r
import java.io.Writer;\r
+import java.math.BigInteger;\r
import java.util.BitSet;\r
\r
-import com.badlogic.gdx.Files.FileType;\r
-import com.badlogic.gdx.Gdx;\r
import com.badlogic.gdx.graphics.GL10;\r
import com.badlogic.gdx.graphics.Sprite;\r
import com.badlogic.gdx.graphics.SpriteBatch;\r
import com.badlogic.gdx.graphics.Texture;\r
-import com.badlogic.gdx.graphics.Texture.TextureFilter;\r
-import com.badlogic.gdx.graphics.Texture.TextureWrap;\r
import com.badlogic.gdx.utils.MathUtils;\r
\r
// BOZO - Support point particles?\r
// BOZO - Add a duplicate emitter button.\r
\r
public class ParticleEmitter {\r
+ static private final int UPDATE_SCALE = 1 << 0;\r
+ static private final int UPDATE_ANGLE = 1 << 1;\r
+ static private final int UPDATE_ROTATION = 1 << 2;\r
+ static private final int UPDATE_VELOCITY = 1 << 3;\r
+ static private final int UPDATE_WIND = 1 << 4;\r
+ static private final int UPDATE_GRAVITY = 1 << 5;\r
+ static private final int UPDATE_TINT = 1 << 6;\r
+\r
private RangedNumericValue delayValue = new RangedNumericValue();\r
private ScaledNumericValue lifeOffsetValue = new ScaledNumericValue();\r
private RangedNumericValue durationValue = new RangedNumericValue();\r
private ScaledNumericValue lifeValue = new ScaledNumericValue();\r
private ScaledNumericValue emissionValue = new ScaledNumericValue();\r
- private ScaledNumericValue sizeValue = new ScaledNumericValue();\r
+ private ScaledNumericValue scaleValue = new ScaledNumericValue();\r
private ScaledNumericValue rotationValue = new ScaledNumericValue();\r
private ScaledNumericValue velocityValue = new ScaledNumericValue();\r
private ScaledNumericValue angleValue = new ScaledNumericValue();\r
private BitSet active;\r
private boolean firstUpdate;\r
private boolean flipX, flipY;\r
+ private int updateFlags;\r
\r
private float emission, emissionDiff, emissionDelta;\r
private float lifeOffset, lifeOffsetDiff;\r
\r
lifeValue.setHigh(1, 1);\r
\r
- sizeValue.setHigh(32, 32);\r
+ scaleValue.setHigh(32, 32);\r
\r
rotationValue.setLow(1, 360);\r
rotationValue.setHigh(180, 180);\r
emissionValue.load(emitter.emissionValue);\r
lifeValue.load(emitter.lifeValue);\r
lifeOffsetValue.load(emitter.lifeOffsetValue);\r
- sizeValue.load(emitter.sizeValue);\r
+ scaleValue.load(emitter.scaleValue);\r
rotationValue.load(emitter.rotationValue);\r
velocityValue.load(emitter.velocityValue);\r
angleValue.load(emitter.angleValue);\r
durationValue.setAlwaysActive(true);\r
emissionValue.setAlwaysActive(true);\r
lifeValue.setAlwaysActive(true);\r
- sizeValue.setAlwaysActive(true);\r
+ scaleValue.setAlwaysActive(true);\r
transparencyValue.setAlwaysActive(true);\r
spawnShapeValue.setAlwaysActive(true);\r
spawnWidthValue.setAlwaysActive(true);\r
spawnHeight = (int)spawnHeightValue.newLowValue();\r
spawnHeightDiff = (int)spawnHeightValue.newHighValue();\r
if (!spawnHeightValue.isRelative()) spawnHeightDiff -= spawnHeight;\r
+\r
+ updateFlags = 0;\r
+ if (angleValue.active && angleValue.timeline.length > 1) updateFlags |= UPDATE_ANGLE;\r
+ if (velocityValue.active && velocityValue.active) updateFlags |= UPDATE_VELOCITY;\r
+ if (scaleValue.timeline.length > 1) updateFlags |= UPDATE_SCALE;\r
+ if (rotationValue.active && rotationValue.timeline.length > 1) updateFlags |= UPDATE_ROTATION;\r
+ if (windValue.active && windValue.timeline.length > 1) updateFlags |= UPDATE_WIND;\r
+ if (gravityValue.active && gravityValue.timeline.length > 1) updateFlags |= UPDATE_GRAVITY;\r
+ if (tintValue.timeline.length > 1) updateFlags |= UPDATE_TINT;\r
}\r
\r
public void activateParticle (int index) {\r
}\r
\r
float percent = durationTimer / (float)duration;\r
+ int updateFlags = this.updateFlags;\r
\r
float offsetTime = lifeOffset + lifeOffsetDiff * lifeOffsetValue.getScale(percent);\r
particle.life = particle.currentLife = life + lifeDiff * lifeValue.getScale(percent);\r
particle.angle = angleValue.newLowValue();\r
particle.angleDiff = angleValue.newHighValue();\r
if (!angleValue.isRelative()) particle.angleDiff -= particle.angle;\r
+ if ((updateFlags & UPDATE_ANGLE) == 0) particle.angle = particle.angle + particle.angleDiff * angleValue.getScale(0);\r
\r
- particle.size = sizeValue.newLowValue() / texture.getWidth();\r
- particle.sizeDiff = sizeValue.newHighValue() / texture.getWidth();\r
- if (!sizeValue.isRelative()) particle.sizeDiff -= particle.size;\r
+ particle.scale = scaleValue.newLowValue() / texture.getWidth();\r
+ particle.scaleDiff = scaleValue.newHighValue() / texture.getWidth();\r
+ if (!scaleValue.isRelative()) particle.scaleDiff -= particle.scale;\r
+ if ((updateFlags & UPDATE_SCALE) == 0) particle.setScale(particle.scale + particle.scaleDiff * scaleValue.getScale(0));\r
\r
if (rotationValue.active) {\r
- particle.rotation = particle.currentRotation = rotationValue.newLowValue();\r
+ particle.rotation = rotationValue.newLowValue();\r
particle.rotationDiff = rotationValue.newHighValue();\r
if (!rotationValue.isRelative()) particle.rotationDiff -= particle.rotation;\r
+ if ((updateFlags & UPDATE_ROTATION) == 0)\r
+ particle.setRotation(particle.rotation + particle.rotationDiff * rotationValue.getScale(0));\r
}\r
\r
if (windValue.active) {\r
if (!gravityValue.isRelative()) particle.gravityDiff -= particle.gravity;\r
}\r
\r
+ if ((updateFlags & UPDATE_TINT) == 0) {\r
+ float[] color = particle.tint;\r
+ if (color == null) particle.tint = color = new float[3];\r
+ float[] temp = tintValue.getColor(0);\r
+ color[0] = temp[0];\r
+ color[1] = temp[1];\r
+ color[2] = temp[2];\r
+ }\r
+\r
particle.transparency = transparencyValue.newLowValue();\r
particle.transparencyDiff = transparencyValue.newHighValue() - particle.transparency;\r
\r
}\r
}\r
\r
- particle.setRotation(particle.currentRotation);\r
particle.setBounds(x - texture.getWidth() / 2, y - texture.getHeight() / 2, texture.getWidth(), texture.getHeight());\r
-\r
- updateParticle(index, offsetTime);\r
}\r
\r
public boolean updateParticle (int index, float delta) {\r
particle.currentLife = life;\r
\r
float percent = 1 - particle.currentLife / particle.life;\r
+ int updateFlags = this.updateFlags;\r
\r
- particle.setScale(particle.size + particle.sizeDiff * sizeValue.getScale(percent));\r
+ if ((updateFlags & UPDATE_SCALE) != 0)\r
+ particle.setScale(particle.scale + particle.scaleDiff * scaleValue.getScale(percent));\r
\r
- float angle = particle.angle + particle.angleDiff * angleValue.getScale(percent);\r
+ float angle = particle.angle;\r
+ if ((updateFlags & UPDATE_ANGLE) != 0) angle += particle.angleDiff * angleValue.getScale(percent);\r
\r
- if (rotationValue.active) {\r
+ if ((updateFlags & UPDATE_ROTATION) != 0) {\r
float rotation = particle.rotation + particle.rotationDiff * rotationValue.getScale(percent);\r
if (aligned) rotation += angle;\r
- rotation -= particle.currentRotation;\r
- if (rotation != 0) {\r
- particle.currentRotation += rotation;\r
- particle.rotate(rotation);\r
- }\r
+ if (rotation != 0) particle.setRotation(rotation);\r
}\r
\r
- if (velocityValue.active) {\r
+ if ((updateFlags & UPDATE_VELOCITY) != 0) {\r
float velocity = (particle.velocity + particle.velocityDiff * velocityValue.getScale(percent)) * delta;\r
- float velocityX = velocity;\r
- float velocityY = velocity;\r
- if (angleValue.active) {\r
- velocityX *= MathUtils.cosDeg(angle);\r
- velocityY *= MathUtils.sinDeg(angle);\r
- }\r
- if (windValue.active) velocityX += (particle.wind + particle.windDiff * windValue.getScale(percent)) * delta;\r
- if (gravityValue.active)\r
+ float velocityX = velocity * MathUtils.cosDeg(angle);\r
+ float velocityY = velocity * MathUtils.sinDeg(angle);\r
+ if ((updateFlags & UPDATE_WIND) != 0)\r
+ velocityX += (particle.wind + particle.windDiff * windValue.getScale(percent)) * delta;\r
+ if ((updateFlags & UPDATE_GRAVITY) != 0)\r
velocityY += (particle.gravity + particle.gravityDiff * gravityValue.getScale(percent)) * delta;\r
particle.translate(velocityX, velocityY);\r
}\r
\r
- float[] color = tintValue.getColor(percent);\r
+ float[] color;\r
+ if ((updateFlags & UPDATE_TINT) != 0)\r
+ color = tintValue.getColor(percent);\r
+ else\r
+ color = particle.tint;\r
particle.setColor(color[0], color[1], color[2],\r
particle.transparency + particle.transparencyDiff * transparencyValue.getScale(percent));\r
\r
return lifeValue;\r
}\r
\r
- public ScaledNumericValue getSize () {\r
- return sizeValue;\r
+ public ScaledNumericValue getScale () {\r
+ return scaleValue;\r
}\r
\r
public ScaledNumericValue getRotation () {\r
spawnWidthValue.save(output);\r
output.write("- Spawn Height - \n");\r
spawnHeightValue.save(output);\r
- output.write("- Size - \n");\r
- sizeValue.save(output);\r
+ output.write("- Scale - \n");\r
+ scaleValue.save(output);\r
output.write("- Velocity - \n");\r
velocityValue.save(output);\r
output.write("- Angle - \n");\r
reader.readLine();\r
spawnHeightValue.load(reader);\r
reader.readLine();\r
- sizeValue.load(reader);\r
+ scaleValue.load(reader);\r
reader.readLine();\r
velocityValue.load(reader);\r
reader.readLine();\r
\r
static class Particle extends Sprite {\r
float life, currentLife;\r
- float size, sizeDiff;\r
- float rotation, currentRotation, rotationDiff;\r
+ float scale, scaleDiff;\r
+ float rotation, rotationDiff;\r
float velocity, velocityDiff;\r
float angle, angleDiff;\r
float transparency, transparencyDiff;\r
float wind, windDiff;\r
float gravity, gravityDiff;\r
+ float[] tint;\r
\r
public Particle (Texture texture) {\r
super(texture);\r
\r
static public class ScaledNumericValue extends RangedNumericValue {\r
private float[] scaling = {1};\r
- private float[] timeline = {0};\r
+ float[] timeline = {0};\r
private float highMin, highMax;\r
private boolean relative;\r
\r
static private float[] temp = new float[4];\r
\r
private float[] colors = {1, 1, 1};\r
- private float[] timeline = {0};\r
+ float[] timeline = {0};\r
\r
public GradientColorValue () {\r
alwaysActive = true;\r
import com.badlogic.gdx.graphics.Texture.TextureWrap;\r
import com.badlogic.gdx.graphics.particles.ParticleEffect;\r
import com.badlogic.gdx.graphics.particles.ParticleEmitter;\r
+import com.badlogic.gdx.math.Matrix4;\r
import com.badlogic.gdx.tests.utils.GdxTest;\r
\r
public class ParticleEmitterTest implements GdxTest {\r
private SpriteBatch spriteBatch;\r
- private BitmapFont font;\r
ParticleEffect effect;\r
int emitterIndex;\r
ArrayList<ParticleEmitter> emitters;\r
int particleCount = 10;\r
+ float fpsCounter;\r
\r
public void surfaceCreated () {\r
if (spriteBatch != null) return;\r
spriteBatch = new SpriteBatch();\r
\r
- font = new BitmapFont(Gdx.files.getFileHandle("data/verdana39.fnt", FileType.Internal), Gdx.files.getFileHandle(\r
- "data/verdana39.png", FileType.Internal), false);\r
-\r
effect = new ParticleEffect();\r
effect.load(Gdx.files.getFileHandle("data/test.p", FileType.Internal), "data", FileType.Internal);\r
effect.setPosition(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2);\r
+ // Of course, a ParticleEffect is normally just used, without messing around with its emitters.\r
emitters = new ArrayList(effect.getEmitters());\r
effect.getEmitters().clear();\r
effect.getEmitters().add(emitters.get(0));\r
particleCount += 5;\r
else if (keycode == Input.Keys.KEYCODE_DPAD_DOWN)\r
particleCount -= 5;\r
- else if (keycode == Input.Keys.KEYCODE_SPACE)\r
+ else if (keycode == Input.Keys.KEYCODE_SPACE) {\r
emitterIndex = (emitterIndex + 1) % emitters.size();\r
- else\r
+ emitter = emitters.get(emitterIndex);\r
+ particleCount = (int)(emitter.getEmission().getHighMax() * emitter.getLife().getHighMax());\r
+ } else\r
return false;\r
particleCount = Math.max(0, particleCount);\r
if (particleCount > emitter.getMaxParticleCount()) emitter.setMaxParticleCount(particleCount * 2);\r
emitter.getEmission().setHigh(particleCount / emitter.getLife().getHighMax());\r
effect.getEmitters().clear();\r
- effect.getEmitters().add(emitters.get(emitterIndex));\r
+ effect.getEmitters().add(emitter);\r
return false;\r
}\r
});\r
}\r
\r
public void surfaceChanged (int width, int height) {\r
+ spriteBatch.getProjectionMatrix().setToOrtho2D(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());\r
}\r
\r
public void render () {\r
+ float delta = Gdx.graphics.getDeltaTime();\r
GL10 gl = Gdx.graphics.getGL10();\r
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);\r
spriteBatch.begin();\r
- effect.draw(spriteBatch, Gdx.graphics.getDeltaTime());\r
- font.draw(spriteBatch, Gdx.graphics.getFramesPerSecond() + " fps", 5, 40, Color.WHITE);\r
- int activeCount = emitters.get(emitterIndex).getActiveCount();\r
- font.draw(spriteBatch, activeCount + "/" + particleCount + " particles", 5, Gdx.graphics.getHeight() - 5, Color.WHITE);\r
+ effect.draw(spriteBatch, delta);\r
spriteBatch.end();\r
+ fpsCounter += delta;\r
+ if (fpsCounter > 3) {\r
+ fpsCounter = 0;\r
+ int activeCount = emitters.get(emitterIndex).getActiveCount();\r
+ System.out.println(activeCount + "/" + particleCount + " particles, FPS: " + Gdx.graphics.getFramesPerSecond());\r
+ }\r
}\r
\r
public void dispose () {\r