1 /******************************************************************************* 2 * Copyright 2011 See AUTHORS file. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 ******************************************************************************/ 16 17 package com.badlogic.gdx.graphics.g2d; 18 19 import java.io.BufferedReader; 20 import java.io.File; 21 import java.io.IOException; 22 import java.io.InputStream; 23 import java.io.InputStreamReader; 24 import java.io.Writer; 25 import java.util.HashMap; 26 27 import com.badlogic.gdx.files.FileHandle; 28 import com.badlogic.gdx.graphics.Texture; 29 import com.badlogic.gdx.math.collision.BoundingBox; 30 import com.badlogic.gdx.utils.Array; 31 import com.badlogic.gdx.utils.Disposable; 32 import com.badlogic.gdx.utils.GdxRuntimeException; 33 import com.badlogic.gdx.utils.StreamUtils; 34 35 /** See <a href="http://www.badlogicgames.com/wordpress/?p=1255">http://www.badlogicgames.com/wordpress/?p=1255</a> 36 * @author mzechner */ 37 public class ParticleEffect implements Disposable { 38 private final Array<ParticleEmitter> emitters; 39 private BoundingBox bounds; 40 private boolean ownsTexture; 41 42 public ParticleEffect () { 43 emitters = new Array(8); 44 } 45 46 public ParticleEffect (ParticleEffect effect) { 47 emitters = new Array(true, effect.emitters.size); 48 for (int i = 0, n = effect.emitters.size; i < n; i++) 49 emitters.add(new ParticleEmitter(effect.emitters.get(i))); 50 } 51 52 public void start () { 53 for (int i = 0, n = emitters.size; i < n; i++) 54 emitters.get(i).start(); 55 } 56 57 public void reset () { 58 for (int i = 0, n = emitters.size; i < n; i++) 59 emitters.get(i).reset(); 60 } 61 62 public void update (float delta) { 63 for (int i = 0, n = emitters.size; i < n; i++) 64 emitters.get(i).update(delta); 65 } 66 67 public void draw (Batch spriteBatch) { 68 for (int i = 0, n = emitters.size; i < n; i++) 69 emitters.get(i).draw(spriteBatch); 70 } 71 72 public void draw (Batch spriteBatch, float delta) { 73 for (int i = 0, n = emitters.size; i < n; i++) 74 emitters.get(i).draw(spriteBatch, delta); 75 } 76 77 public void allowCompletion () { 78 for (int i = 0, n = emitters.size; i < n; i++) 79 emitters.get(i).allowCompletion(); 80 } 81 82 public boolean isComplete () { 83 for (int i = 0, n = emitters.size; i < n; i++) { 84 ParticleEmitter emitter = emitters.get(i); 85 if (!emitter.isComplete()) return false; 86 } 87 return true; 88 } 89 90 public void setDuration (int duration) { 91 for (int i = 0, n = emitters.size; i < n; i++) { 92 ParticleEmitter emitter = emitters.get(i); 93 emitter.setContinuous(false); 94 emitter.duration = duration; 95 emitter.durationTimer = 0; 96 } 97 } 98 99 public void setPosition (float x, float y) { 100 for (int i = 0, n = emitters.size; i < n; i++) 101 emitters.get(i).setPosition(x, y); 102 } 103 104 public void setFlip (boolean flipX, boolean flipY) { 105 for (int i = 0, n = emitters.size; i < n; i++) 106 emitters.get(i).setFlip(flipX, flipY); 107 } 108 109 public void flipY () { 110 for (int i = 0, n = emitters.size; i < n; i++) 111 emitters.get(i).flipY(); 112 } 113 114 public Array<ParticleEmitter> getEmitters () { 115 return emitters; 116 } 117 118 /** Returns the emitter with the specified name, or null. */ 119 public ParticleEmitter findEmitter (String name) { 120 for (int i = 0, n = emitters.size; i < n; i++) { 121 ParticleEmitter emitter = emitters.get(i); 122 if (emitter.getName().equals(name)) return emitter; 123 } 124 return null; 125 } 126 127 public void save (Writer output) throws IOException { 128 int index = 0; 129 for (int i = 0, n = emitters.size; i < n; i++) { 130 ParticleEmitter emitter = emitters.get(i); 131 if (index++ > 0) output.write("\n\n"); 132 emitter.save(output); 133 } 134 } 135 136 public void load (FileHandle effectFile, FileHandle imagesDir) { 137 loadEmitters(effectFile); 138 loadEmitterImages(imagesDir); 139 } 140 141 public void load (FileHandle effectFile, TextureAtlas atlas) { 142 load(effectFile, atlas, null); 143 } 144 145 public void load (FileHandle effectFile, TextureAtlas atlas, String atlasPrefix) { 146 loadEmitters(effectFile); 147 loadEmitterImages(atlas, atlasPrefix); 148 } 149 150 public void loadEmitters (FileHandle effectFile) { 151 InputStream input = effectFile.read(); 152 emitters.clear(); 153 BufferedReader reader = null; 154 try { 155 reader = new BufferedReader(new InputStreamReader(input), 512); 156 while (true) { 157 ParticleEmitter emitter = new ParticleEmitter(reader); 158 emitters.add(emitter); 159 if (reader.readLine() == null) break; 160 if (reader.readLine() == null) break; 161 } 162 } catch (IOException ex) { 163 throw new GdxRuntimeException("Error loading effect: " + effectFile, ex); 164 } finally { 165 StreamUtils.closeQuietly(reader); 166 } 167 } 168 169 public void loadEmitterImages (TextureAtlas atlas) { 170 loadEmitterImages(atlas, null); 171 } 172 173 public void loadEmitterImages (TextureAtlas atlas, String atlasPrefix) { 174 for (int i = 0, n = emitters.size; i < n; i++) { 175 ParticleEmitter emitter = emitters.get(i); 176 String imagePath = emitter.getImagePath(); 177 if (imagePath == null) continue; 178 String imageName = new File(imagePath.replace('\\', '/')).getName(); 179 int lastDotIndex = imageName.lastIndexOf('.'); 180 if (lastDotIndex != -1) imageName = imageName.substring(0, lastDotIndex); 181 if (atlasPrefix != null) imageName = atlasPrefix + imageName; 182 Sprite sprite = atlas.createSprite(imageName); 183 if (sprite == null) throw new IllegalArgumentException("SpriteSheet missing image: " + imageName); 184 emitter.setSprite(sprite); 185 } 186 } 187 188 public void loadEmitterImages (FileHandle imagesDir) { 189 ownsTexture = true; 190 HashMap<String, Sprite> loadedSprites = new HashMap<String, Sprite>(emitters.size); 191 for (int i = 0, n = emitters.size; i < n; i++) { 192 ParticleEmitter emitter = emitters.get(i); 193 String imagePath = emitter.getImagePath(); 194 if (imagePath == null) continue; 195 String imageName = new File(imagePath.replace('\\', '/')).getName(); 196 Sprite sprite = loadedSprites.get(imageName); 197 if (sprite == null) { 198 sprite = new Sprite(loadTexture(imagesDir.child(imageName))); 199 loadedSprites.put(imageName, sprite); 200 } 201 emitter.setSprite(sprite); 202 } 203 } 204 205 protected Texture loadTexture (FileHandle file) { 206 return new Texture(file, false); 207 } 208 209 /** Disposes the texture for each sprite for each ParticleEmitter. */ 210 public void dispose () { 211 if (!ownsTexture) return; 212 for (int i = 0, n = emitters.size; i < n; i++) { 213 ParticleEmitter emitter = emitters.get(i); 214 emitter.getSprite().getTexture().dispose(); 215 } 216 } 217 218 /** Returns the bounding box for all active particles. z axis will always be zero. */ 219 public BoundingBox getBoundingBox () { 220 if (bounds == null) bounds = new BoundingBox(); 221 222 BoundingBox bounds = this.bounds; 223 bounds.inf(); 224 for (ParticleEmitter emitter : this.emitters) 225 bounds.ext(emitter.getBoundingBox()); 226 return bounds; 227 } 228 229 public void scaleEffect (float scaleFactor) { 230 for (ParticleEmitter particleEmitter : emitters) { 231 particleEmitter.getScale().setHigh(particleEmitter.getScale().getHighMin() * scaleFactor, 232 particleEmitter.getScale().getHighMax() * scaleFactor); 233 particleEmitter.getScale().setLow(particleEmitter.getScale().getLowMin() * scaleFactor, 234 particleEmitter.getScale().getLowMax() * scaleFactor); 235 236 particleEmitter.getVelocity().setHigh(particleEmitter.getVelocity().getHighMin() * scaleFactor, 237 particleEmitter.getVelocity().getHighMax() * scaleFactor); 238 particleEmitter.getVelocity().setLow(particleEmitter.getVelocity().getLowMin() * scaleFactor, 239 particleEmitter.getVelocity().getLowMax() * scaleFactor); 240 241 particleEmitter.getGravity().setHigh(particleEmitter.getGravity().getHighMin() * scaleFactor, 242 particleEmitter.getGravity().getHighMax() * scaleFactor); 243 particleEmitter.getGravity().setLow(particleEmitter.getGravity().getLowMin() * scaleFactor, 244 particleEmitter.getGravity().getLowMax() * scaleFactor); 245 246 particleEmitter.getWind().setHigh(particleEmitter.getWind().getHighMin() * scaleFactor, 247 particleEmitter.getWind().getHighMax() * scaleFactor); 248 particleEmitter.getWind().setLow(particleEmitter.getWind().getLowMin() * scaleFactor, 249 particleEmitter.getWind().getLowMax() * scaleFactor); 250 251 particleEmitter.getSpawnWidth().setHigh(particleEmitter.getSpawnWidth().getHighMin() * scaleFactor, 252 particleEmitter.getSpawnWidth().getHighMax() * scaleFactor); 253 particleEmitter.getSpawnWidth().setLow(particleEmitter.getSpawnWidth().getLowMin() * scaleFactor, 254 particleEmitter.getSpawnWidth().getLowMax() * scaleFactor); 255 256 particleEmitter.getSpawnHeight().setHigh(particleEmitter.getSpawnHeight().getHighMin() * scaleFactor, 257 particleEmitter.getSpawnHeight().getHighMax() * scaleFactor); 258 particleEmitter.getSpawnHeight().setLow(particleEmitter.getSpawnHeight().getLowMin() * scaleFactor, 259 particleEmitter.getSpawnHeight().getLowMax() * scaleFactor); 260 261 particleEmitter.getXOffsetValue().setLow(particleEmitter.getXOffsetValue().getLowMin() * scaleFactor, 262 particleEmitter.getXOffsetValue().getLowMax() * scaleFactor); 263 264 particleEmitter.getYOffsetValue().setLow(particleEmitter.getYOffsetValue().getLowMin() * scaleFactor, 265 particleEmitter.getYOffsetValue().getLowMax() * scaleFactor); 266 } 267 } 268 269 /** Sets the {@link com.badlogic.gdx.graphics.g2d.ParticleEmitter#setCleansUpBlendFunction(boolean) cleansUpBlendFunction} 270 * parameter on all {@link com.badlogic.gdx.graphics.g2d.ParticleEmitter ParticleEmitters} currently in this ParticleEffect. 271 * <p> 272 * IMPORTANT: If set to false and if the next object to use this Batch expects alpha blending, you are responsible for setting 273 * the Batch's blend function to (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) before that next object is drawn. 274 * @param cleanUpBlendFunction */ 275 public void setEmittersCleanUpBlendFunction (boolean cleanUpBlendFunction) { 276 for (int i = 0, n = emitters.size; i < n; i++) { 277 emitters.get(i).setCleansUpBlendFunction(cleanUpBlendFunction); 278 } 279 } 280 } 281