haxepunk/graphics/emitter/BaseEmitter.hx
package haxepunk.graphics.emitter;
import haxepunk.utils.BlendMode;
import haxepunk.HXP;
import haxepunk.Graphic;
import haxepunk.utils.Color;
import haxepunk.utils.Ease.EaseFunction;
import haxepunk.math.MathUtil;
import haxepunk.math.Random;
import haxepunk.math.Vector2;
@:generic class BaseEmitter<T:Graphic> extends Graphic
{
/**
* Amount of currently existing particles.
*/
public var particleCount(default, null):Int;
public var scale:Float = 1;
function new(source:T)
{
super();
_source = source;
_sourceIsImage = Std.isOfType(_source, Image);
_types = new Map<String, ParticleType>();
active = true;
particleCount = 0;
}
override public function render(point:Vector2, camera:Camera)
{
var p:Particle = _particle;
var t:Float,
pt:Float,
type:ParticleType,
td:Float,
atd:Float,
std:Float,
rtd:Float,
ctd:Float;
// loop through the particles
while (p != null)
{
// get time scale
t = p._time / p._duration;
if (p._firstDraw)
{
p._ox = point.x;
p._oy = point.y;
p._firstDraw = false;
}
// get particle type
type = p._type;
_source.smooth = smooth;
_source.flexibleLayer = flexibleLayer;
_source.pixelSnapping = pixelSnapping;
_source.blend = type._blendMode == null ? this.blend : type._blendMode;
var n:Int = type._trailLength;
while (n >= 0)
{
pt = p._time - (n--) * type._trailDelay;
t = pt / p._duration;
if (t < 0 || pt >= p._stopTime) continue;
td = type._ease != null ? type._ease(t) : t;
updateParticle(p, td);
ctd = type._colorEase != null ? type._colorEase(t) : t;
_source.color = p.color(ctd);
atd = type._alphaEase != null ? type._alphaEase(t) : t;
_source.alpha = p.alpha(atd) * Math.pow(type._trailAlpha, n);
if (_sourceIsImage)
{
var _source:Image = cast _source;
rtd = type._rotationEase != null ? type._rotationEase(t) : t;
_source.angle = p.angle(rtd);
std = type._scaleEase != null ? type._scaleEase(t) : t;
_source.scale = scale * p.scale(std);
}
_source.x = p.x(td) - point.x + this.x - originX;
_source.y = p.y(td) - point.y + this.y - originY;
_source.render(point, camera);
}
// get next particle
p = p._next;
}
}
function updateParticle(p:Particle, td:Float) {}
override public function update()
{
// quit if there are no particles
if (_particle == null) return;
// particle info
var p:Particle = _particle,
n:Particle;
// loop through the particles
while (p != null)
{
p._time += HXP.elapsed; // Update particle time elapsed
var type = p._type;
var t = p._time / p._duration;
if (p._time - (type._trailLength * type._trailDelay) >= p._stopTime)
{
if (p._next != null) p._next._prev = p._prev;
if (p._prev != null) p._prev._next = p._next;
else _particle = p._next;
n = p._next;
p._next = _cache;
p._prev = null;
_cache = p;
p = n;
particleCount--;
continue;
}
// get next particle
p = p._next;
}
}
/**
* Clears all particles.
* @since 2.5.2
*/
public function clear()
{
// quit if there are no particles
if (_particle == null)
{
return;
}
// particle info
var p:Particle = _particle,
n:Particle;
// loop through the particles
while (p != null)
{
// move this particle to the cache
n = p._next;
p._next = _cache;
p._prev = null;
_cache = p;
p = n;
particleCount--;
}
_particle = null;
}
/**
* Resets originX/Y to 0 since there are no dimensions.
*/
override public function centerOrigin():Void
{
originX = originY = 0;
}
/**
* Creates a new Particle type for this Emitter.
* @param name Name of the particle type.
* @param frames Array of frame indices for the particles to animate.
* @return A new ParticleType object.
*/
function addType(name:String, ?blendMode:BlendMode):ParticleType
{
if (blendMode == null) blendMode = this.blend;
var pt:ParticleType = _types.get(name);
if (pt != null)
throw "Cannot add multiple particle types of the same name";
pt = new ParticleType(name, blendMode);
_types.set(name, pt);
return pt;
}
/**
* Emits a particle.
* @param name Particle type to emit.
* @param x X point to emit from.
* @param y Y point to emit from.
* @param angle Base angle to start from.
* @return The Particle emited.
*/
public function emit(name:String, x:Float = 0, y:Float = 0, angle:Float = 0):Particle
{
var p:Particle, type:ParticleType = _types.get(name);
if (type == null)
throw "Particle type \"" + name + "\" does not exist.";
if (_cache != null)
{
p = _cache;
_cache = p._next;
}
else
{
p = new Particle();
}
p._next = _particle;
p._prev = null;
if (p._next != null) p._next._prev = p;
p._type = type;
p._time = 0;
p._duration = type._duration + type._durationRange * Random.random;
p._stopTime = p._duration;
p._angle = angle + type._angle + type._angleRange * Random.random;
p._startAngle = type._startAngle + type._startAngleRange * Random.random;
p._spanAngle = type._spanAngle + type._spanAngleRange * Random.random;
var d:Float = type._distance + type._distanceRange * Random.random;
p._moveX = Math.cos(p._angle * MathUtil.RAD) * d;
p._moveY = Math.sin(p._angle * MathUtil.RAD) * d;
p._x = x;
p._y = y;
p._gravity = type._gravity + type._gravityRange * Random.random;
p._firstDraw = true;
p._ox = p._oy = 0;
particleCount++;
return (_particle = p);
}
/**
* Randomly emits the particle inside the specified radius
* @param name Particle type to emit.
* @param x X point to emit from.
* @param y Y point to emit from.
* @param radius Radius to emit inside.
*
* @return The Particle emited.
*/
public function emitInCircle(name:String, x:Float, y:Float, radius:Float):Particle
{
var angle = Random.random * Math.PI * 2;
radius *= Random.random;
return emit(name, x + Math.cos(angle) * radius, y + Math.sin(angle) * radius);
}
/**
* Randomly emits the particle inside the specified area
* @param name Particle type to emit
* @param x X point to emit from.
* @param y Y point to emit from.
* @param width Width of the area to emit from.
* @param height height of the area to emit from.
*
* @return The Particle emited.
*/
public function emitInRectangle(name:String, x:Float, y:Float, width:Float, height:Float):Particle
{
return emit(name, x + Random.random * width, y + Random.random * height);
}
/**
* Defines the motion range for a particle type.
* @param name The particle type.
* @param angle Launch Direction.
* @param distance Distance to travel.
* @param duration Particle duration.
* @param angleRange Random amount to add to the particle's direction.
* @param distanceRange Random amount to add to the particle's distance.
* @param durationRange Random amount to add to the particle's duration.
* @param ease Optional ease function.
* @param backwards If the motion should be played backwards.
* @return This ParticleType object.
*/
public function setMotion(name:String, angle:Float, distance:Float, duration:Float, angleRange:Float = 0, distanceRange:Float = 0, durationRange:Float = 0, ?ease:EaseFunction, backwards:Bool = false):ParticleType
{
var pt:ParticleType = _types.get(name);
if (pt == null) return null;
return pt.setMotion(angle, distance, duration, angleRange, distanceRange, durationRange, ease, backwards);
}
/**
* Sets the gravity range for a particle type.
* @param name The particle type.
* @param gravity Gravity amount to affect to the particle y velocity.
* @param gravityRange Random amount to add to the particle's gravity.
* @return This ParticleType object.
*/
public function setGravity(name:String, ?gravity:Float = 0, ?gravityRange:Float = 0):ParticleType
{
return _types.get(name).setGravity(gravity, gravityRange);
}
/**
* Sets the alpha range of the particle type.
* @param name The particle type.
* @param start The starting alpha.
* @param finish The finish alpha.
* @param ease Optional easer function.
* @return This ParticleType object.
*/
public function setAlpha(name:String, start:Float = 1, finish:Float = 0, ?ease:EaseFunction):ParticleType
{
var pt:ParticleType = _types.get(name);
if (pt == null) return null;
return pt.setAlpha(start, finish, ease);
}
/**
* Sets the scale range of the particle type.
* @param name The particle type.
* @param start The starting scale.
* @param finish The finish scale.
* @param ease Optional easer function.
* @return This ParticleType object.
* @since 2.6.0
*/
public function setScale(name:String, start:Float = 1, finish:Float = 0, ?ease:EaseFunction):ParticleType
{
var pt:ParticleType = _types.get(name);
if (pt == null) return null;
return pt.setScale(start, finish, ease);
}
/**
* Defines the rotation range for a particle type.
* @param name The particle type.
* @param startAngle Starting angle.
* @param spanAngle Total amount of degrees to rotate.
* @param startAngleRange Random amount to add to the particle's starting angle.
* @param spanAngleRange Random amount to add to the particle's span angle.
* @param ease Optional easer function.
* @since 2.6.0
* @return This ParticleType object.
*/
public function setRotation(name:String, startAngle:Float, spanAngle:Float, startAngleRange:Float = 0, spanAngleRange:Float = 0, ?ease:EaseFunction):ParticleType
{
var pt:ParticleType = _types.get(name);
if (pt == null) return null;
return pt.setRotation(startAngle, spanAngle, startAngleRange, spanAngleRange, ease);
}
/**
* Sets the trail of the particle type.
* @param name The particle type.
* @param length Number of trailing particles to draw.
* @param delay Time to delay each trailing particle, in seconds.
* @param alpha Multiply each successive trail particle's alpha by this amount.
* @since 2.6.0
* @return This ParticleType object.
*/
public function setTrail(name:String, length:Int = 1, delay:Float = 0.1, alpha:Float=1):ParticleType
{
var pt:ParticleType = _types.get(name);
if (pt == null) return null;
return pt.setTrail(length, delay, alpha);
}
/**
* Sets the color range of the particle type.
* @param name The particle type.
* @param start The starting color.
* @param finish The finish color.
* @param ease Optional easer function.
* @return This ParticleType object.
*/
public function setColor(name:String, start:Color = Color.White, finish:Color = Color.Black, ?ease:EaseFunction):ParticleType
{
var pt:ParticleType = _types.get(name);
if (pt == null) return null;
return pt.setColor(start, finish, ease);
}
// Particle information.
var _source:T;
var _sourceIsImage:Bool;
var _types:Map<String, ParticleType>;
var _particle:Particle;
var _cache:Particle;
}