package com.junerking.particle; import java.util.HashMap; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.GL10; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.TextureAtlas; import com.badlogic.gdx.graphics.g2d.TextureRegion; // typedef void (*CC_UPDATE_PARTICLE_IMP)(id, SEL, tCCParticle*, CGPoint); /** * Particle System base class Attributes of a Particle System: - emmision rate of the particles - Gravity Mode (Mode A): * - gravity - direction - speed +- variance - tangential acceleration +- variance - radial acceleration +- variance - * Radius Mode (Mode B): - startRadius +- variance - endRadius +- variance - rotate +- variance - Properties common to * all modes: - life +- life variance - start spin +- variance - end spin +- variance - start size +- variance - end * size +- variance - start color +- variance - end color +- variance - life +- variance - blending function - tt * * cocos2d also supports particles generated by Particle Designer (http://particledesigner.71squared.com/). 'Radius * Mode' in Particle Designer uses a fixed emit rate of 30 hz. Since that can't be guarateed in cocos2d, cocos2d uses a * another approach, but the results are almost identical. * * cocos2d supports all the variables used by Particle Designer plus a bit more: - spinning particles (supported when * using CCQuadParticleSystem) - tangential acceleration (Gravity mode) - radial acceleration (Gravity mode) - radius * direction (Radius mode) (Particle Designer supports outwards to inwards direction only) * * It is possible to customize any of the above mentioned properties in runtime. Example: * * @code emitter.radialAccel = 15; emitter.startSpin = 0; * @endcode */ public abstract class CCParticleSystem { /** The Particle emitter lives forever */ public static final int kCCParticleDurationInfinity = -1; /** The starting size of the particle is equal to the ending size */ public static final int kCCParticleStartSizeEqualToEndSize = -1; /** The starting radius of the particle is equal to the ending radius */ public static final int kCCParticleStartRadiusEqualToEndRadius = -1; // backward compatible public static final int kParticleStartSizeEqualToEndSize = kCCParticleStartSizeEqualToEndSize; public static final int kParticleDurationInfinity = kCCParticleDurationInfinity; /** Gravity mode (A mode) */ public static final int kCCParticleModeGravity = 0; /** Radius mode (B mode) */ public static final int kCCParticleModeRadius = 1; /** * @typedef tCCPositionType possible types of particle positions */ /** If the emitter is repositioned, the living particles won't be repositioned */ public static final int kCCPositionTypeFree = 0; /** * Living particles are attached to the world but will follow the emitter repositioning. Use case: Attach an emitter * to an sprite, and you want that the emitter follows the sprite. */ public static final int kCCPositionTypeRelative = 1; /** If the emitter is repositioned, the living particles will be repositioned too */ public static final int kCCPositionTypeGrouped = 2; /** * @struct tCCParticle Structure that contains the values of each particle */ static class CCParticle { static class ParticleModeA { CGPoint dir = new CGPoint(); float radialAccel; float tangentialAccel; } // Mode B: radius mode static class ParticleModeB { float angle; float degreesPerSecond; float radius; float deltaRadius; } CGPoint pos = new CGPoint(); CGPoint startPos = new CGPoint(); Color color = new Color(); Color deltaColor = new Color(); float size; float deltaSize; float rotation; float deltaRotation; float timeToLive; ParticleModeA modeA; ParticleModeB modeB; } // Mode A:Gravity + Tangential Accel + Radial Accel class ModeA { // gravity of the particles CGPoint gravity = CGPoint.zero(); // The speed the particles will have. float speed; // The speed variance float speedVar; // Tangential acceleration float tangentialAccel; // Tangential acceleration variance float tangentialAccelVar; // Radial acceleration float radialAccel; // Radial acceleration variance float radialAccelVar; }; // Mode B: circular movement (gravity, radial accel and tangential accel don't are not used in this mode) class ModeB { // The starting radius of the particles float startRadius; // The starting radius variance of the particles float startRadiusVar; // The ending radius of the particles float endRadius; // The ending radius variance of the particles float endRadiusVar; // Number of degress to rotate a particle around the source pos per second float rotatePerSecond; // Variance in degrees for rotatePerSecond float rotatePerSecondVar; }; protected int id; // Optimization //Method updateParticleImp; // String updateParticleSel; // is the particle system active ? protected boolean active; // duration in seconds of the system. -1 is infinity protected float duration; // time elapsed since the start of the system (in seconds) protected float elapsed; // start ize of the particles float startSize; public void setStartSize(float s) { startSize = s; } // start Size variance float startSizeVar; public void setStartSizeVar(float ssv) { startSizeVar = ssv; } // End size of the particle float endSize; public void setEndSize(float s) { endSize = s; } // end size of variance float endSizeVar; public void setEndSizeVar(float esv) { endSizeVar = esv; } // start angle of the particles float startSpin; public void setStartSpin(float s) { startSpin = s; } // start angle variance float startSpinVar; public void setStartSpinVar(float ssv) { startSpinVar = ssv; } // End angle of the particle float endSpin; public void setEndSpin(float es) { endSpin = es; } // end angle ariance float endSpinVar; public void setEndSpinVar(float esv) { endSpinVar = esv; } /// Gravity of the particles protected CGPoint centerOfGravity = CGPoint.zero(); public void setCenterOfGravity(CGPoint p) { centerOfGravity = CGPoint.make(p.x, p.y); } public CGPoint getCenterOfGravity() { return CGPoint.ccp(centerOfGravity.x, centerOfGravity.y); } protected CGPoint position_ = new CGPoint(); public void setPosition(CGPoint pos) { position_.set(pos); } public void setPosition(float x, float y) { position_.set(x, y); } public CGPoint getCGPoint() { return position_; } // position is from "superclass" CocosNode // Emitter source position protected CGPoint source = CGPoint.zero(); // Position variance protected CGPoint posVar = CGPoint.zero(); public void setPosVar(CGPoint pv) { posVar = CGPoint.make(pv.x, pv.y); } // The angle (direction) of the particles measured in degrees protected float angle; public void setAngle(float a) { angle = a; } // Angle variance measured in degrees; protected float angleVar; public void setAngleVar(float av) { angleVar = av; } // Size of the particles protected float size; // Size variance protected float sizeVar; // How many seconds will the particle live protected float life; // Life variance protected float lifeVar; public void setLifeVar(float lv) { lifeVar = lv; } // Start color of the particles protected Color startColor = new Color(); public void setStartColor(Color sc) { startColor.set(sc); } public Color getStartColor() { return new Color(startColor); } public Color getStartColorRef() { return startColor; } // Start color variance protected Color startColorVar = new Color(); public void setStartColorVar(Color scv) { startColorVar.set(scv); } public Color getStartColorVar() { return new Color(startColorVar); } public Color getStartColorVarRef() { return startColorVar; } // End color of the particles protected Color endColor = new Color(); public void setEndColor(Color ec) { endColor.set(ec); } public Color getEndColorRef() { return endColor; } // End color variance protected Color endColorVar = new Color(); public void setEndColorVar(Color ecv) { endColorVar.set(ecv); } public Color getEndColorVarRef() { return endColorVar; } // blend function ccBlendFunc blendFunc = new ccBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA); // movment type: free or grouped // protected int positionType; // Whether or not the node will be auto-removed when there are not particles protected boolean autoRemoveOnFinish_; // Array of particles protected CCParticle particles[]; // Maximum particles protected int totalParticles; public int getTotalParticles() { return totalParticles; } // Count of active particles protected int particleCount; // // additive color or blend // protected boolean blendAdditive; // color modulate protected boolean colorModulate; // How many particles can be emitted per second protected float emissionRate; public void setEmissionRate(float er) { emissionRate = er; } protected float emitCounter; // Different modes int emitterMode = -1; public void setEmitterMode(int em) { if (emitterMode == em) return; emitterMode = em; if (em == kCCParticleModeGravity) { modeA = new ModeA(); if (modeB != null) modeB = null; } else { modeB = new ModeB(); if (modeA != null) modeA = null; } } ModeA modeA; ModeB modeB; // Array of colors //CCColorF colors[]; // Array of pointsizes //float pointsizes[]; // vertices buffer id protected int verticesID = -1; // colors buffer id protected int colorsID; // particle idx protected int particleIdx; public void setAutoRemoveOnFinish(boolean ar) { autoRemoveOnFinish_ = ar; } //! whether or not the system is full public boolean isFull() { return (particleCount == totalParticles); } public void setTangentialAccel(float t) { assert (emitterMode == kCCParticleModeGravity) : "Particle Mode should be Gravity"; modeA.tangentialAccel = t; } public float getTangentialAccel() { assert emitterMode == kCCParticleModeGravity : "Particle Mode should be Gravity"; return modeA.tangentialAccel; } public void setTangentialAccelVar(float t) { assert emitterMode == kCCParticleModeGravity : "Particle Mode should be Gravity"; modeA.tangentialAccelVar = t; } public float getTangentialAccelVar() { assert emitterMode == kCCParticleModeGravity : "Particle Mode should be Gravity"; return modeA.tangentialAccelVar; } public void setRadialAccel(float t) { assert emitterMode == kCCParticleModeGravity : "Particle Mode should be Gravity"; modeA.radialAccel = t; } public float getRadialAccel() { assert emitterMode == kCCParticleModeGravity : "Particle Mode should be Gravity"; return modeA.radialAccel; } public void setRadialAccelVar(float t) { assert emitterMode == kCCParticleModeGravity : "Particle Mode should be Gravity"; modeA.radialAccelVar = t; } public float getRadialAccelVar() { assert emitterMode == kCCParticleModeGravity : "Particle Mode should be Gravity"; return modeA.radialAccelVar; } public void setGravity(CGPoint g) { assert emitterMode == kCCParticleModeGravity : "Particle Mode should be Gravity"; modeA.gravity.set(g); } /** * Gravity value */ public CGPoint getGravity() { return modeA.gravity; } public CGPoint gravity() { assert emitterMode == kCCParticleModeGravity : "Particle Mode should be Gravity"; return modeA.gravity; } public void setSpeed(float speed) { assert emitterMode == kCCParticleModeGravity : "Particle Mode should be Gravity"; modeA.speed = speed; } public float getSpeed() { assert emitterMode == kCCParticleModeGravity : "Particle Mode should be Gravity"; return modeA.speed; } public void setSpeedVar(float speedVar) { assert emitterMode == kCCParticleModeGravity : "Particle Mode should be Gravity"; modeA.speedVar = speedVar; } public float getSpeedVar() { assert emitterMode == kCCParticleModeGravity : "Particle Mode should be Gravity"; return modeA.speedVar; } public void setStartRadius(float startRadius) { assert emitterMode == kCCParticleModeRadius : "Particle Mode should be Radius"; modeB.startRadius = startRadius; } public float startRadius() { assert emitterMode == kCCParticleModeRadius : "Particle Mode should be Radius"; return modeB.startRadius; } public void setStartRadiusVar(float startRadiusVar) { assert emitterMode == kCCParticleModeRadius : "Particle Mode should be Radius"; modeB.startRadiusVar = startRadiusVar; } public float startRadiusVar() { assert emitterMode == kCCParticleModeRadius : "Particle Mode should be Radius"; return modeB.startRadiusVar; } public void setEndRadius(float endRadius) { assert emitterMode == kCCParticleModeRadius : "Particle Mode should be Radius"; modeB.endRadius = endRadius; } public float endRadius() { assert emitterMode == kCCParticleModeRadius : "Particle Mode should be Radius"; return modeB.endRadius; } public void setEndRadiusVar(float endRadiusVar) { assert emitterMode == kCCParticleModeRadius : "Particle Mode should be Radius"; modeB.endRadiusVar = endRadiusVar; } public float endRadiusVar() { assert emitterMode == kCCParticleModeRadius : "Particle Mode should be Radius"; return modeB.endRadiusVar; } /** * Is the emitter active */ public boolean getActive() { return active; } /** * Quantity of particles that are being simulated at the moment */ public int getParticleCount() { return particleCount; } public void setRotatePerSecond(float degrees) { // NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); modeB.rotatePerSecond = degrees; } public float rotatePerSecond() { // NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); return modeB.rotatePerSecond; } public void setRotatePerSecondVar(float degrees) { // NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); modeB.rotatePerSecondVar = degrees; } public float rotatePerSecondVar() { // NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); return modeB.rotatePerSecondVar; } /** * How many seconds the emitter wil run. -1 means 'forever' */ public float getDuration() { return duration; } public void setDuration(float duration) { this.duration = duration; } /** * Source location of particles respective to emitter location */ public CGPoint getSource() { return source; } public void setSource(CGPoint source) { this.source = source; } /** * Position variance of the emitter */ public CGPoint getPosVar() { return posVar; } /** * life, and life variation of each particle */ public float getLife() { return life; } public void setLife(float life) { this.life = life; } // movement type: free or grouped private int positionType_; public int getPositionType() { return positionType_; } public void setPositionType(int type) { positionType_ = type; } /** * tt used to render the particles */ protected String texture_name; private TextureRegion tt; public TextureRegion getTexture() { return tt; } public void setTexture(TextureRegion tex) { tt = tex; } //! Initializes a system with a fixed number of particles protected CCParticleSystem(int numberOfParticles) { initWithNumberOfParticles(numberOfParticles); } protected CCParticleSystem() { } protected void initWithNumberOfParticles(int numberOfParticles) { totalParticles = numberOfParticles; particles = new CCParticle[totalParticles]; for (int i = 0; i < totalParticles; i++) { particles[i] = new CCParticle(); } // default, active active = true; // default movement type; positionType_ = kCCPositionTypeFree; // by default be in mode A: this.setEmitterMode(kCCParticleModeGravity); // default: modulate // XXX: not used // colorModulate = YES; autoRemoveOnFinish_ = false; // profiling // Optimization: compile udpateParticle method /* updateParticleSel = "updateQuad"; // updateParticleImp = null; try { updateParticleImp = this.getClass().getMethod(updateParticleSel, new Class[]{CCParticle.class, CGPoint.class}); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); }*/ // udpate after action in run! // this.scheduleUpdate(1);//TODO } private void initParticle(CCParticle particle) { // timeToLive // no negative life. prevent division by 0 particle.timeToLive = Math.max(0, life + lifeVar * ccMacros.CCRANDOM_MINUS1_1()); // position particle.pos.set(centerOfGravity.x + posVar.x * ccMacros.CCRANDOM_MINUS1_1(), centerOfGravity.y + posVar.y * ccMacros.CCRANDOM_MINUS1_1()); // Color // Color start = new Color(); float start_r = Math.min(1, Math.max(0, startColor.r + startColorVar.r * ccMacros.CCRANDOM_MINUS1_1())); float start_g = Math.min(1, Math.max(0, startColor.g + startColorVar.g * ccMacros.CCRANDOM_MINUS1_1())); float start_b = Math.min(1, Math.max(0, startColor.b + startColorVar.b * ccMacros.CCRANDOM_MINUS1_1())); float start_a = Math.min(1, Math.max(0, startColor.a + startColorVar.a * ccMacros.CCRANDOM_MINUS1_1())); // Color end = new Color(); float end_r = Math.min(1, Math.max(0, endColor.r + endColorVar.r * ccMacros.CCRANDOM_MINUS1_1())); float end_g = Math.min(1, Math.max(0, endColor.g + endColorVar.g * ccMacros.CCRANDOM_MINUS1_1())); float end_b = Math.min(1, Math.max(0, endColor.b + endColorVar.b * ccMacros.CCRANDOM_MINUS1_1())); float end_a = Math.min(1, Math.max(0, endColor.a + endColorVar.a * ccMacros.CCRANDOM_MINUS1_1())); ccColor4FUtil.set(particle.color, start_r, start_g, start_b, start_a); ccColor4FUtil.set(particle.deltaColor, (end_r - start_r) / particle.timeToLive, (end_g - start_g) / particle.timeToLive, (end_b - start_b) / particle.timeToLive, (end_a - start_a) / particle.timeToLive); // size float startS = Math.max(0, startSize + startSizeVar * ccMacros.CCRANDOM_MINUS1_1()); // no negative size particle.size = startS; if (endSize == kCCParticleStartSizeEqualToEndSize) particle.deltaSize = 0; else { float endS = endSize + endSizeVar * ccMacros.CCRANDOM_MINUS1_1(); endS = Math.max(0, endS); particle.deltaSize = (endS - startS) / particle.timeToLive; } // rotation float startA = startSpin + startSpinVar * ccMacros.CCRANDOM_MINUS1_1(); float endA = endSpin + endSpinVar * ccMacros.CCRANDOM_MINUS1_1(); particle.rotation = startA; particle.deltaRotation = (endA - startA) / particle.timeToLive; // position if (positionType_ == kCCPositionTypeFree) { particle.startPos.set(0, 0); } else if (positionType_ == kCCPositionTypeRelative) { particle.startPos.set(position_); } // direction float a = ccMacros.CC_DEGREES_TO_RADIANS(angle + angleVar * ccMacros.CCRANDOM_MINUS1_1()); // Mode Gravity: A if (emitterMode == kCCParticleModeGravity) { float s = modeA.speed + modeA.speedVar * ccMacros.CCRANDOM_MINUS1_1(); if (particle.modeA == null) { particle.modeA = new CCParticle.ParticleModeA(); } // direction particle.modeA.dir.set((float) Math.cos(a), (float) Math.sin(a)); CGPointUtil.mult(particle.modeA.dir, s); // radial accel particle.modeA.radialAccel = modeA.radialAccel + modeA.radialAccelVar * ccMacros.CCRANDOM_MINUS1_1(); // tangential accel particle.modeA.tangentialAccel = modeA.tangentialAccel + modeA.tangentialAccelVar * ccMacros.CCRANDOM_MINUS1_1(); } // Mode Radius: B else { // Set the default diameter of the particle from the source position float startRadius = modeB.startRadius + modeB.startRadiusVar * ccMacros.CCRANDOM_MINUS1_1(); float endRadius = modeB.endRadius + modeB.endRadiusVar * ccMacros.CCRANDOM_MINUS1_1(); if (particle.modeB == null) { particle.modeB = new CCParticle.ParticleModeB(); } particle.modeB.radius = startRadius; if (modeB.endRadius == kCCParticleStartRadiusEqualToEndRadius) particle.modeB.deltaRadius = 0; else particle.modeB.deltaRadius = (endRadius - startRadius) / particle.timeToLive; particle.modeB.angle = a; particle.modeB.degreesPerSecond = ccMacros.CC_DEGREES_TO_RADIANS(modeB.rotatePerSecond + modeB.rotatePerSecondVar * ccMacros.CCRANDOM_MINUS1_1()); } } //! stop emitting particles. Running particles will continue to run until they die public void stopSystem() { active = false; elapsed = duration; emitCounter = 0; } //! Kill all living particles. public void resetSystem() { active = true; elapsed = 0; for (particleIdx = 0; particleIdx < particleCount; ++particleIdx) { CCParticle p = particles[particleIdx]; p.timeToLive = 0; } isCompleted = false; } /** * creates an initializes a CCParticleSystem from a plist file. This plist files can be creted manually or with * Particle Designer: http://particledesigner.71squared.com/ * * @since v0.99.3 */ public static CCParticleSystem particleWithFile(String plistFile) { // return new CCParticleSystem(plistFile); return null; } public void loadParticleFile(HashMap<String, Object> dictionary, TextureAtlas atlas) { assert (dictionary != null) : "A dictionary object is expected."; int maxParticles = Integer.parseInt((String) (dictionary.get("maxParticles"))); initWithNumberOfParticles(maxParticles); // angle setAngle(Float.parseFloat((String) dictionary.get("angle"))); setAngleVar(Float.parseFloat((String) dictionary.get("angleVariance"))); // duration setDuration(Float.parseFloat((String) dictionary.get("duration"))); // color float r, g, b, a; r = Float.parseFloat((String) dictionary.get("startColorRed")); g = Float.parseFloat((String) dictionary.get("startColorGreen")); b = Float.parseFloat((String) dictionary.get("startColorBlue")); a = Float.parseFloat((String) dictionary.get("startColorAlpha")); setStartColor(new Color(r, g, b, a)); r = Float.parseFloat((String) dictionary.get("startColorVarianceRed")); g = Float.parseFloat((String) dictionary.get("startColorVarianceGreen")); b = Float.parseFloat((String) dictionary.get("startColorVarianceBlue")); a = Float.parseFloat((String) dictionary.get("startColorVarianceAlpha")); setStartColorVar(new Color(r, g, b, a)); r = Float.parseFloat((String) dictionary.get("finishColorRed")); g = Float.parseFloat((String) dictionary.get("finishColorGreen")); b = Float.parseFloat((String) dictionary.get("finishColorBlue")); a = Float.parseFloat((String) dictionary.get("finishColorAlpha")); setEndColor(new Color(r, g, b, a)); r = Float.parseFloat((String) dictionary.get("finishColorVarianceRed")); g = Float.parseFloat((String) dictionary.get("finishColorVarianceGreen")); b = Float.parseFloat((String) dictionary.get("finishColorVarianceBlue")); a = Float.parseFloat((String) dictionary.get("finishColorVarianceAlpha")); setEndColorVar(new Color(r, g, b, a)); // particle size setStartSize(Float.parseFloat((String) dictionary.get("startParticleSize"))); setStartSizeVar(Float.parseFloat((String) dictionary.get("startParticleSizeVariance"))); setEndSize(Float.parseFloat((String) dictionary.get("finishParticleSize"))); setEndSizeVar(Float.parseFloat((String) dictionary.get("finishParticleSizeVariance"))); // position float x = Float.parseFloat((String) dictionary.get("sourcePositionx")); float y = Float.parseFloat((String) dictionary.get("sourcePositiony")); setPosition(CGPoint.ccp(x, y)); setPosVar(CGPoint.ccp(Float.parseFloat((String) dictionary.get("sourcePositionVariancex")), Float.parseFloat((String) dictionary.get("sourcePositionVariancey")))); setEmitterMode((int) (Float.parseFloat((String) dictionary.get("emitterType")))); if (emitterMode == kCCParticleModeGravity) { // Mode A: Gravity + tangential accel + radial accel // gravity setGravity(CGPoint.ccp(Float.parseFloat((String) dictionary.get("gravityx")), Float.parseFloat((String) dictionary.get("gravityy")))); // // speed setSpeed(Float.parseFloat((String) dictionary.get("speed"))); setSpeedVar(Float.parseFloat((String) dictionary.get("speedVariance"))); // radial acceleration setRadialAccel(Float.parseFloat((String) dictionary.get("radialAcceleration"))); setRadialAccelVar(Float.parseFloat((String) dictionary.get("radialAccelVariance"))); // tangential acceleration setTangentialAccel(Float.parseFloat((String) dictionary.get("tangentialAcceleration"))); setTangentialAccelVar(Float.parseFloat((String) dictionary.get("tangentialAccelVariance"))); } else { float maxRadius = Float.parseFloat((String) dictionary.get("maxRadius")); float maxRadiusVar = Float.parseFloat((String) dictionary.get("maxRadiusVariance")); float minRadius = Float.parseFloat((String) dictionary.get("minRadius")); setStartRadius(maxRadius); setStartRadiusVar(maxRadiusVar); setEndRadius(minRadius); setEndRadiusVar(0); setRotatePerSecond(Float.parseFloat((String) dictionary.get("rotatePerSecond"))); setRotatePerSecondVar(Float.parseFloat((String) dictionary.get("rotatePerSecondVariance"))); } // life span setLife(Float.parseFloat((String) dictionary.get("particleLifespan"))); setLifeVar(Float.parseFloat((String) dictionary.get("particleLifespanVariance"))); // emission Rate setEmissionRate(getTotalParticles() / getLife()); texture_name = (String) dictionary.get("textureFileName"); if (atlas != null) tt = atlas.findRegion(texture_name); // // tt // // Try to get the tt from the cache // String textureName = (String) dictionary.get("textureFileName"); // String textureData = (String) dictionary.get("textureImageData"); // // boolean loaded = false; // try { // Texture tex = new Texture(textureName); // setTexture(new TextureRegion(tex)); // loaded = true; // } catch (Exception e) { // e.printStackTrace(); // } } //! Add a particle to the emitter public boolean addParticle() { if (isFull()) return false; CCParticle particle = particles[particleCount]; initParticle(particle); particleCount++; return true; } OneClassPool<CGPoint> pointPool = new OneClassPool<CGPoint>() { @Override protected CGPoint allocate() { return new CGPoint(); } }; public void update(float dt) { if (active && emissionRate != 0) { float rate = 1.0f / emissionRate; emitCounter += dt; while (particleCount < totalParticles && emitCounter > rate) { addParticle(); emitCounter -= rate; } elapsed += dt; if (duration != -1 && duration < elapsed) stopSystem(); } particleIdx = 0; CGPoint currentPosition = pointPool.get(); CGPoint tmp = pointPool.get(); CGPoint radial = pointPool.get(); CGPoint tangential = pointPool.get(); if (positionType_ == kCCPositionTypeFree) { currentPosition.set(CGPoint.zero()); } else if (positionType_ == kCCPositionTypeRelative) { currentPosition.set(position_); } while (particleIdx < particleCount) { CCParticle p = particles[particleIdx]; // life p.timeToLive -= dt; if (p.timeToLive > 0) { // Mode A: gravity, direction, tangential accel & radial accel if (emitterMode == kCCParticleModeGravity) { CGPointUtil.zero(radial); // radial acceleration if (p.pos.x != 0 || p.pos.y != 0) CGPointUtil.normalize(p.pos, radial); tangential.set(radial); CGPointUtil.mult(radial, p.modeA.radialAccel); // tangential acceleration float newy = tangential.x; tangential.x = -tangential.y; tangential.y = newy; CGPointUtil.mult(tangential, p.modeA.tangentialAccel); // (gravity + radial + tangential) * dt CGPointUtil.add(radial, tangential, tmp); CGPointUtil.add(tmp, modeA.gravity); CGPointUtil.mult(tmp, dt); CGPointUtil.add(p.modeA.dir, tmp); CGPointUtil.mult(p.modeA.dir, dt, tmp); CGPointUtil.add(p.pos, tmp); } // Mode B: radius movement else { // Update the angle and radius of the particle. p.modeB.angle += p.modeB.degreesPerSecond * dt; p.modeB.radius += p.modeB.deltaRadius * dt; p.pos.x = -(float) Math.cos(p.modeB.angle) * p.modeB.radius; p.pos.y = -(float) Math.sin(p.modeB.angle) * p.modeB.radius; } // color p.color.r += (p.deltaColor.r * dt); p.color.g += (p.deltaColor.g * dt); p.color.b += (p.deltaColor.b * dt); p.color.a += (p.deltaColor.a * dt); // size p.size += (p.deltaSize * dt); p.size = Math.max(0, p.size); // angle p.rotation += (p.deltaRotation * dt); // CGPoint newPos; // if (positionType_ == kCCPositionTypeFree || positionType_ == kCCPositionTypeRelative) { // CGPoint diff = tmp; // CGPointUtil.sub(currentPosition, p.startPos, diff); // CGPointUtil.sub(p.pos, diff, diff); // newPos = diff; // } else { // newPos = p.pos; // } // this.updateQuad(p, newPos); particleIdx++; } else { // life < 0 if (particleIdx != particleCount - 1) { CCParticle tmpPart = particles[particleIdx]; particles[particleIdx] = particles[particleCount - 1]; particles[particleCount - 1] = tmpPart; } particleCount--; if (particleCount == 0) { isCompleted = true; if (autoRemoveOnFinish_) { //TODO: 从stage中移除此node } } } } pointPool.free(currentPosition); pointPool.free(tmp); pointPool.free(radial); pointPool.free(tangential); postStep(); } protected boolean isCompleted = false; public boolean isCompleted() { return isCompleted; } //! should be overriden by subclasses public void updateQuad(CCParticle particle, CGPoint pos) { } public void draw(SpriteBatch sprite_batch, float parent_alpha) { sprite_batch.setBlendFunction(blendFunc.src, blendFunc.dst); particleIdx = 0; while (particleIdx < particleCount) { CCParticle p = particles[particleIdx++]; sprite_batch.setColor(p.color.r, p.color.g, p.color.b, p.color.a); float size_2 = p.size / 2; if (p.rotation != 0) { float x1 = -size_2; float y1 = -size_2; float x2 = size_2; float y2 = size_2; float x = p.pos.x + p.startPos.x; float y = p.pos.y + p.startPos.y; float r = (float) -ccMacros.CC_DEGREES_TO_RADIANS(p.rotation); float cr = (float) Math.cos(r); float sr = (float) Math.sin(r); float ax = x1 * cr - y1 * sr + x; float ay = x1 * sr + y1 * cr + y; float bx = x2 * cr - y1 * sr + x; float by = x2 * sr + y1 * cr + y; float cx = x2 * cr - y2 * sr + x; float cy = x2 * sr + y2 * cr + y; float dx = x1 * cr - y2 * sr + x; float dy = x1 * sr + y2 * cr + y; //System.out.println(ax + " " + ay + " " + bx + " " + by + " " + cx + " " + cy + " " + dx + " " + dy); sprite_batch.draw(tt, ax, ay, bx, by, dx, dy, cx, cy, tt.getU(), tt.getV(), tt.getU2(), tt.getV2()); } else { float x = p.pos.x + p.startPos.x; float y = p.pos.y + p.startPos.y; // bottom-left vertex: float ax = x - size_2; float ay = y - size_2; // bottom-right vertex: float bx = x + size_2; float by = y - size_2; // top-left vertex: float cx = x - size_2; float cy = y + size_2; // top-right vertex: float dx = x + size_2; float dy = y + size_2; //System.out.println(ax + " " + ay + " " + bx + " " + by + " " + cx + " " + cy + " " + dx + " " + dy); sprite_batch.draw(tt, ax, ay, bx, by, dx, dy, cx, cy, tt.getU(), tt.getV(), tt.getU2(), tt.getV2()); } } } public void postStep() { // should be overriden } public void setBlendAdditive(boolean additive) { if (additive) { blendFunc.src = GL10.GL_SRC_ALPHA; blendFunc.dst = GL10.GL_ONE; } else { blendFunc.src = GL10.GL_SRC_ALPHA; blendFunc.dst = GL10.GL_ONE_MINUS_SRC_ALPHA; } } public boolean getBlendAdditive() { return (blendFunc.src == GL10.GL_SRC_ALPHA && blendFunc.dst == GL10.GL_ONE); } }