package org.flixel; import com.badlogic.gdx.utils.reflect.ClassReflection; /** * <code>FlxEmitter</code> is a lightweight particle emitter. * It can be used for one-time explosions or for * continuous fx like rain and fire. <code>FlxEmitter</code> * is not optimized or anything; all it does is launch * <code>FlxParticle</code> objects out at set intervals * by setting their positions and velocities accordingly. * It is easy to use and relatively efficient, * relying on <code>FlxGroup</code>'s RECYCLE POWERS. * * @author Ka Wing Chin */ public class FlxEmitter extends FlxGroup { /** * The X position of the top left corner of the emitter in world space. */ public float x; /** * The Y position of the top left corner of emitter in world space. */ public float y; /** * The width of the emitter. Particles can be randomly generated from anywhere within this box. */ public float width; /** * The height of the emitter. Particles can be randomly generated from anywhere within this box. */ public float height; /** * The minimum possible velocity of a particle. * The default value is (-100,-100). */ public FlxPoint minParticleSpeed; /** * The maximum possible velocity of a particle. * The default value is (100,100). */ public FlxPoint maxParticleSpeed; /** * The X and Y drag component of particles launched from the emitter. */ public FlxPoint particleDrag; /** * The minimum possible angular velocity of a particle. The default value is -360. * NOTE: rotating particles are more expensive to draw than non-rotating ones! */ public float minRotation; /** * The maximum possible angular velocity of a particle. The default value is 360. * NOTE: rotating particles are more expensive to draw than non-rotating ones! */ public float maxRotation; /** * Sets the <code>acceleration.y</code> member of each particle to this value on launch. */ public float gravity; /** * Determines whether the emitter is currently emitting particles. * It is totally safe to directly toggle this. */ public boolean on; /** * How often a particle is emitted (if emitter is started with Explode == false). */ public float frequency; /** * How long each particle lives once it is emitted. * Set lifespan to 'zero' for particles to live forever. */ public float lifespan; /** * How much each particle should bounce. 1 = full bounce, 0 = no bounce. */ public float bounce; /** * Set your own particle class type here. * Default is <code>FlxParticle</code>. */ public Class<? extends FlxParticle> particleClass; /** * Internal helper for deciding how many particles to launch. */ protected int _quantity; /** * Internal helper for the style of particle emission (all at once, or one at a time). */ protected boolean _explode; /** * Internal helper for deciding when to launch particles or kill them. */ protected float _timer; /** * Internal counter for figuring out how many particles to launch. */ protected int _counter; /** * Internal point object, handy for reusing for memory mgmt purposes. */ protected FlxPoint _point; /** * Creates a new <code>FlxEmitter</code> object at a specific position. * Does NOT automatically generate or attach particles! * * @param X The X position of the emitter. * @param Y The Y position of the emitter. * @param Size Optional, specifies a maximum capacity for this emitter. */ public FlxEmitter(float X, float Y, int Size) { super(Size); x = X; y = Y; width = 0; height = 0; minParticleSpeed = new FlxPoint(-100,-100); maxParticleSpeed = new FlxPoint(100,100); minRotation = -360; maxRotation = 360; gravity = 0; particleClass = null; particleDrag = new FlxPoint(); frequency = 0.1f; lifespan = 3; bounce = 0; _quantity = 0; _counter = 0; _explode = true; on = false; _point = new FlxPoint(); } /** * Creates a new <code>FlxEmitter</code> object at a specific position. * Does NOT automatically generate or attach particles! * * @param X The X position of the emitter. * @param Y The Y position of the emitter. */ public FlxEmitter(float X, float Y) { this(X, Y, 0); } /** * Creates a new <code>FlxEmitter</code> object at a specific position. * Does NOT automatically generate or attach particles! * * @param X The X position of the emitter. */ public FlxEmitter(float X) { this(X, 0, 0); } /** * Creates a new <code>FlxEmitter</code> object at a specific position. * Does NOT automatically generate or attach particles! */ public FlxEmitter() { this(0, 0, 0); } /** * Clean up memory. */ @Override public void destroy() { minParticleSpeed = null; maxParticleSpeed = null; particleDrag = null; particleClass = null; _point = null; super.destroy(); } /** * This function generates a new array of particle sprites to attach to the emitter. * * @param Graphics If you opted to not pre-configure an array of FlxParticle objects, you can simply pass in a particle image or sprite sheet. * @param Quantity The number of particles to generate when using the "create from image" option. * @param BakedRotations How many frames of baked rotation to use (boosts performance). Set to zero to not use baked rotations. * @param Multiple Whether the image in the Graphics param is a single particle or a bunch of particles (if it's a bunch, they need to be square!). * @param Collide Whether the particles should be flagged as not 'dead' (non-colliding particles are higher performance). 0 means no collisions, 0-1 controls scale of particle's bounding box. * * @return This FlxEmitter instance (nice for chaining stuff together, if you're into that). */ public FlxEmitter makeParticles(String Graphics, int Quantity, int BakedRotations, boolean Multiple, float Collide) { setMaxSize(Quantity); int totalFrames = 1; if(Multiple) { FlxSprite sprite = new FlxSprite(); sprite.loadGraphic(Graphics,true); totalFrames = sprite.getNumFrames(); sprite.destroy(); } int randomFrame; FlxParticle particle; int i = 0; while(i < Quantity) { if(particleClass == null) particle = new FlxParticle(); else { try { particle = ClassReflection.newInstance(particleClass); } catch(Exception e) { throw new RuntimeException(e); } } if(Multiple) { randomFrame = (int) (FlxG.random()*totalFrames); if(BakedRotations > 0) particle.loadRotatedGraphic(Graphics,BakedRotations,randomFrame); else { particle.loadGraphic(Graphics,true); particle.setFrame(randomFrame); } } else { if(BakedRotations > 0) particle.loadRotatedGraphic(Graphics,BakedRotations); else particle.loadGraphic(Graphics); } if(Collide > 0) { particle.width *= Collide; particle.height *= Collide; particle.centerOffsets(); } else particle.allowCollisions = FlxObject.NONE; particle.exists = false; add(particle); i++; } return this; } /** * This function generates a new array of particle sprites to attach to the emitter. * * @param Graphics If you opted to not pre-configure an array of FlxParticle objects, you can simply pass in a particle image or sprite sheet. * @param Quantity The number of particles to generate when using the "create from image" option. * @param BakedRotations How many frames of baked rotation to use (boosts performance). Set to zero to not use baked rotations. * @param Multiple Whether the image in the Graphics param is a single particle or a bunch of particles (if it's a bunch, they need to be square!). * * @return This FlxEmitter instance (nice for chaining stuff together, if you're into that). */ public FlxEmitter makeParticles(String Graphics, int Quantity, int BakedRotations, boolean Multiple) { return makeParticles(Graphics, Quantity, BakedRotations, Multiple, 0.8f); } /** * This function generates a new array of particle sprites to attach to the emitter. * * @param Graphics If you opted to not pre-configure an array of FlxParticle objects, you can simply pass in a particle image or sprite sheet. * @param Quantity The number of particles to generate when using the "create from image" option. * @param BakedRotations How many frames of baked rotation to use (boosts performance). Set to zero to not use baked rotations. * * @return This FlxEmitter instance (nice for chaining stuff together, if you're into that). */ public FlxEmitter makeParticles(String Graphics, int Quantity, int BakedRotations) { return makeParticles(Graphics, Quantity, BakedRotations, false, 0.8f); } /** * This function generates a new array of particle sprites to attach to the emitter. * * @param Graphics If you opted to not pre-configure an array of FlxParticle objects, you can simply pass in a particle image or sprite sheet. * @param Quantity The number of particles to generate when using the "create from image" option. * * @return This FlxEmitter instance (nice for chaining stuff together, if you're into that). */ public FlxEmitter makeParticles(String Graphics, int Quantity) { return makeParticles(Graphics, Quantity, 16, false, 0.8f); } /** * This function generates a new array of particle sprites to attach to the emitter. * * @param Graphics If you opted to not pre-configure an array of FlxParticle objects, you can simply pass in a particle image or sprite sheet. * * @return This FlxEmitter instance (nice for chaining stuff together, if you're into that). */ public FlxEmitter makeParticles(String Graphics) { return makeParticles(Graphics, 50, 16, false, 0.8f); } /** * Called automatically by the game loop, decides when to launch particles and when to "die". */ @Override public void update() { if(on) { if(_explode) { on = false; int i = 0; int l = _quantity; if((l <= 0) || (l > length)) l = length; while(i < l) { emitParticle(); i++; } _quantity = 0; } else { _timer += FlxG.elapsed; while((frequency > 0) && (_timer > frequency) && on) { _timer -= frequency; emitParticle(); if((_quantity > 0) && (++_counter >= _quantity)) { on = false; _quantity = 0; } } } } super.update(); } /** * Call this function to turn off all the particles and the emitter. */ @Override public void kill() { on = false; super.kill(); } /** * Call this function to start emitting particles. * * @param Explode Whether the particles should all burst out at once. * @param Lifespan How long each particle lives once emitted. 0 = forever. * @param Frequency Ignored if Explode is set to true. Frequency is how often to emit a particle. 0 = never emit, 0.1 = 1 particle every 0.1 seconds, 5 = 1 particle every 5 seconds. * @param Quantity How many particles to launch. 0 = "all of the particles". */ public void start(boolean Explode,float Lifespan,float Frequency,int Quantity) { revive(); visible = true; on = true; _explode = Explode; lifespan = Lifespan; frequency = Frequency; _quantity += Quantity; _counter = 0; _timer = 0; } /** * Call this function to start emitting particles. * * @param Explode Whether the particles should all burst out at once. * @param Lifespan How long each particle lives once emitted. 0 = forever. * @param Frequency Ignored if Explode is set to true. Frequency is how often to emit a particle. 0 = never emit, 0.1 = 1 particle every 0.1 seconds, 5 = 1 particle every 5 seconds. */ public void start(boolean Explode,float Lifespan,float Frequency) { start(Explode,Lifespan,Frequency,0); } /** * Call this function to start emitting particles. * * @param Explode Whether the particles should all burst out at once. * @param Lifespan How long each particle lives once emitted. 0 = forever. */ public void start(boolean Explode,float Lifespan) { start(Explode,Lifespan,0.1f,0); } /** * Call this function to start emitting particles. * * @param Explode Whether the particles should all burst out at once. */ public void start(boolean Explode) { start(Explode,0,0.1f,0); } /** * Call this function to start emitting particles. */ public void start() { start(true,0,0.1f,0); } /** * This function can be used both internally and externally to emit the next particle. */ public void emitParticle() { FlxParticle particle = (FlxParticle) recycle(particleClass); particle.lifespan = lifespan; particle.elasticity = bounce; particle.reset(x - ((int)particle.width>>1) + FlxG.random()*width, y - ((int)particle.height>>1) + FlxG.random()*height); particle.visible = true; if(minParticleSpeed.x != maxParticleSpeed.x) particle.velocity.x = minParticleSpeed.x + FlxG.random()*(maxParticleSpeed.x-minParticleSpeed.x); else particle.velocity.x = minParticleSpeed.x; if(minParticleSpeed.y != maxParticleSpeed.y) particle.velocity.y = minParticleSpeed.y + FlxG.random()*(maxParticleSpeed.y-minParticleSpeed.y); else particle.velocity.y = minParticleSpeed.y; particle.acceleration.y = gravity; if(minRotation != maxRotation) particle.angularVelocity = minRotation + FlxG.random()*(maxRotation-minRotation); else particle.angularVelocity = minRotation; if(particle.angularVelocity != 0) particle.angle = FlxG.random()*360-180; particle.drag.x = particleDrag.x; particle.drag.y = particleDrag.y; particle.onEmit(); } /** * A more compact way of setting the width and height of the emitter. * * @param Width The desired width of the emitter (particles are spawned randomly within these dimensions). * @param Height The desired height of the emitter. */ public void setSize(int Width,int Height) { width = Width; height = Height; } /** * A more compact way of setting the X velocity range of the emitter. * * @param Min The minimum value for this range. * @param Max The maximum value for this range. */ public void setXSpeed(float Min,float Max) { minParticleSpeed.x = Min; maxParticleSpeed.x = Max; } /** * A more compact way of setting the X velocity range of the emitter. * * @param Min The minimum value for this range. */ public void setXSpeed(float Min) { setXSpeed(Min,0); } /** * A more compact way of setting the X velocity range of the emitter. */ public void setXSpeed() { setXSpeed(0,0); } /** * A more compact way of setting the Y velocity range of the emitter. * * @param Min The minimum value for this range. * @param Max The maximum value for this range. */ public void setYSpeed(float Min,float Max) { minParticleSpeed.y = Min; maxParticleSpeed.y = Max; } /** * A more compact way of setting the Y velocity range of the emitter. * * @param Min The minimum value for this range. */ public void setYSpeed(float Min) { setYSpeed(Min,0); } /** * A more compact way of setting the Y velocity range of the emitter. */ public void setYSpeed() { setYSpeed(0,0); } /** * A more compact way of setting the angular velocity constraints of the emitter. * * @param Min The minimum value for this range. * @param Max The maximum value for this range. */ public void setRotation(float Min,float Max) { minRotation = Min; maxRotation = Max; } /** * A more compact way of setting the angular velocity constraints of the emitter. * * @param Min The minimum value for this range. */ public void setRotation(float Min) { setRotation(Min,0); } /** * A more compact way of setting the angular velocity constraints of the emitter. */ public void setRotation() { setRotation(0,0); } /** * Change the emitter's midpoint to match the midpoint of a <code>FlxObject</code>. * * @param Object The <code>FlxObject</code> that you want to sync up with. */ public void at(FlxObject Object) { Object.getMidpoint(_point); x = _point.x - ((int)width>>1); y = _point.y - ((int)height>>1); } }