1 package aurelienribon.tweenengine; 2 3 /** 4 * BaseTween is the base class of Tween and Timeline. It defines the 5 * iteration engine used to play animations for any number of times, and in 6 * any direction, at any speed. 7 * <p/> 8 * 9 * It is responsible for calling the different callbacks at the right moments, 10 * and for making sure that every callbacks are triggered, even if the update 11 * engine gets a big delta time at once. 12 * 13 * @see Tween 14 * @see Timeline 15 * @author Aurelien Ribon | http://www.aurelienribon.com/ 16 */ 17 public abstract class BaseTween<T> { 18 // General 19 private int step; 20 private int repeatCnt; 21 private boolean isIterationStep; 22 private boolean isYoyo; 23 24 // Timings 25 protected float delay; 26 protected float duration; 27 private float repeatDelay; 28 private float currentTime; 29 private float deltaTime; 30 private boolean isStarted; // true when the object is started 31 private boolean isInitialized; // true after the delay 32 private boolean isFinished; // true when all repetitions are done 33 private boolean isKilled; // true if kill() was called 34 private boolean isPaused; // true if pause() was called 35 36 // Misc 37 private TweenCallback callback; 38 private int callbackTriggers; 39 private Object userData; 40 41 // Package access 42 boolean isAutoRemoveEnabled; 43 boolean isAutoStartEnabled; 44 45 // ------------------------------------------------------------------------- 46 47 protected void reset() { 48 step = -2; 49 repeatCnt = 0; 50 isIterationStep = isYoyo = false; 51 52 delay = duration = repeatDelay = currentTime = deltaTime = 0; 53 isStarted = isInitialized = isFinished = isKilled = isPaused = false; 54 55 callback = null; 56 callbackTriggers = TweenCallback.COMPLETE; 57 userData = null; 58 59 isAutoRemoveEnabled = isAutoStartEnabled = true; 60 } 61 62 // ------------------------------------------------------------------------- 63 // Public API 64 // ------------------------------------------------------------------------- 65 66 /** 67 * Builds and validates the object. Only needed if you want to finalize a 68 * tween or timeline without starting it, since a call to ".start()" also 69 * calls this method. 70 * 71 * @return The current object, for chaining instructions. 72 */ 73 public T build() { 74 return (T) this; 75 } 76 77 /** 78 * Starts or restarts the object unmanaged. You will need to take care of 79 * its life-cycle. If you want the tween to be managed for you, use a 80 * {@link TweenManager}. 81 * 82 * @return The current object, for chaining instructions. 83 */ 84 public T start() { 85 build(); 86 currentTime = 0; 87 isStarted = true; 88 return (T) this; 89 } 90 91 /** 92 * Convenience method to add an object to a manager. Its life-cycle will be 93 * handled for you. Relax and enjoy the animation. 94 * 95 * @return The current object, for chaining instructions. 96 */ 97 public T start(TweenManager manager) { 98 manager.add(this); 99 return (T) this; 100 } 101 102 /** 103 * Adds a delay to the tween or timeline. 104 * 105 * @param delay A duration. 106 * @return The current object, for chaining instructions. 107 */ 108 public T delay(float delay) { 109 this.delay += delay; 110 return (T) this; 111 } 112 113 /** 114 * Kills the tween or timeline. If you are using a TweenManager, this object 115 * will be removed automatically. 116 */ 117 public void kill() { 118 isKilled = true; 119 } 120 121 /** 122 * Stops and resets the tween or timeline, and sends it to its pool, for 123 + * later reuse. Note that if you use a {@link TweenManager}, this method 124 + * is automatically called once the animation is finished. 125 */ 126 public void free() { 127 } 128 129 /** 130 * Pauses the tween or timeline. Further update calls won't have any effect. 131 */ 132 public void pause() { 133 isPaused = true; 134 } 135 136 /** 137 * Resumes the tween or timeline. Has no effect is it was no already paused. 138 */ 139 public void resume() { 140 isPaused = false; 141 } 142 143 /** 144 * Repeats the tween or timeline for a given number of times. 145 * @param count The number of repetitions. For infinite repetition, 146 * use Tween.INFINITY, or a negative number. 147 * 148 * @param delay A delay between each iteration. 149 * @return The current tween or timeline, for chaining instructions. 150 */ 151 public T repeat(int count, float delay) { 152 if (isStarted) throw new RuntimeException("You can't change the repetitions of a tween or timeline once it is started"); 153 repeatCnt = count; 154 repeatDelay = delay >= 0 ? delay : 0; 155 isYoyo = false; 156 return (T) this; 157 } 158 159 /** 160 * Repeats the tween or timeline for a given number of times. 161 * Every two iterations, it will be played backwards. 162 * 163 * @param count The number of repetitions. For infinite repetition, 164 * use Tween.INFINITY, or '-1'. 165 * @param delay A delay before each repetition. 166 * @return The current tween or timeline, for chaining instructions. 167 */ 168 public T repeatYoyo(int count, float delay) { 169 if (isStarted) throw new RuntimeException("You can't change the repetitions of a tween or timeline once it is started"); 170 repeatCnt = count; 171 repeatDelay = delay >= 0 ? delay : 0; 172 isYoyo = true; 173 return (T) this; 174 } 175 176 /** 177 * Sets the callback. By default, it will be fired at the completion of the 178 * tween or timeline (event COMPLETE). If you want to change this behavior 179 * and add more triggers, use the {@link setCallbackTriggers()} method. 180 * 181 * @see TweenCallback 182 */ 183 public T setCallback(TweenCallback callback) { 184 this.callback = callback; 185 return (T) this; 186 } 187 188 /** 189 * Changes the triggers of the callback. The available triggers, listed as 190 * members of the {@link TweenCallback} interface, are: 191 * <p/> 192 * 193 * <b>BEGIN</b>: right after the delay (if any)<br/> 194 * <b>START</b>: at each iteration beginning<br/> 195 * <b>END</b>: at each iteration ending, before the repeat delay<br/> 196 * <b>COMPLETE</b>: at last END event<br/> 197 * <b>BACK_BEGIN</b>: at the beginning of the first backward iteration<br/> 198 * <b>BACK_START</b>: at each backward iteration beginning, after the repeat delay<br/> 199 * <b>BACK_END</b>: at each backward iteration ending<br/> 200 * <b>BACK_COMPLETE</b>: at last BACK_END event 201 * <p/> 202 * 203 * <pre> {@code 204 * forward : BEGIN COMPLETE 205 * forward : START END START END START END 206 * |--------------[XXXXXXXXXX]------[XXXXXXXXXX]------[XXXXXXXXXX] 207 * backward: bEND bSTART bEND bSTART bEND bSTART 208 * backward: bCOMPLETE bBEGIN 209 * }</pre> 210 * 211 * @param flags one or more triggers, separated by the '|' operator. 212 * @see TweenCallback 213 */ 214 public T setCallbackTriggers(int flags) { 215 this.callbackTriggers = flags; 216 return (T) this; 217 } 218 219 /** 220 * Attaches an object to this tween or timeline. It can be useful in order 221 * to retrieve some data from a TweenCallback. 222 * 223 * @param data Any kind of object. 224 * @return The current tween or timeline, for chaining instructions. 225 */ 226 public T setUserData(Object data) { 227 userData = data; 228 return (T) this; 229 } 230 231 // ------------------------------------------------------------------------- 232 // Getters 233 // ------------------------------------------------------------------------- 234 235 /** 236 * Gets the delay of the tween or timeline. Nothing will happen before 237 * this delay. 238 */ 239 public float getDelay() { 240 return delay; 241 } 242 243 /** 244 * Gets the duration of a single iteration. 245 */ 246 public float getDuration() { 247 return duration; 248 } 249 250 /** 251 * Gets the number of iterations that will be played. 252 */ 253 public int getRepeatCount() { 254 return repeatCnt; 255 } 256 257 /** 258 * Gets the delay occuring between two iterations. 259 */ 260 public float getRepeatDelay() { 261 return repeatDelay; 262 } 263 264 /** 265 * Returns the complete duration, including initial delay and repetitions. 266 * The formula is as follows: 267 * <pre> 268 * fullDuration = delay + duration + (repeatDelay + duration) * repeatCnt 269 * </pre> 270 */ 271 public float getFullDuration() { 272 if (repeatCnt < 0) return -1; 273 return delay + duration + (repeatDelay + duration) * repeatCnt; 274 } 275 276 /** 277 * Gets the attached data, or null if none. 278 */ 279 public Object getUserData() { 280 return userData; 281 } 282 283 /** 284 * Gets the id of the current step. Values are as follows:<br/> 285 * <ul> 286 * <li>even numbers mean that an iteration is playing,<br/> 287 * <li>odd numbers mean that we are between two iterations,<br/> 288 * <li>-2 means that the initial delay has not ended,<br/> 289 * <li>-1 means that we are before the first iteration,<br/> 290 * <li>repeatCount*2 + 1 means that we are after the last iteration 291 */ 292 public int getStep() { 293 return step; 294 } 295 296 /** 297 * Gets the local time. 298 */ 299 public float getCurrentTime() { 300 return currentTime; 301 } 302 303 /** 304 * Returns true if the tween or timeline has been started. 305 */ 306 public boolean isStarted() { 307 return isStarted; 308 } 309 310 /** 311 * Returns true if the tween or timeline has been initialized. Starting 312 * values for tweens are stored at initialization time. This initialization 313 * takes place right after the initial delay, if any. 314 */ 315 public boolean isInitialized() { 316 return isInitialized; 317 } 318 319 /** 320 * Returns true if the tween is finished (i.e. if the tween has reached 321 * its end or has been killed). If you don't use a TweenManager, you may 322 * want to call {@link free()} to reuse the object later. 323 */ 324 public boolean isFinished() { 325 return isFinished || isKilled; 326 } 327 328 /** 329 * Returns true if the iterations are played as yoyo. Yoyo means that 330 * every two iterations, the animation will be played backwards. 331 */ 332 public boolean isYoyo() { 333 return isYoyo; 334 } 335 336 /** 337 * Returns true if the tween or timeline is currently paused. 338 */ 339 public boolean isPaused() { 340 return isPaused; 341 } 342 343 // ------------------------------------------------------------------------- 344 // Abstract API 345 // ------------------------------------------------------------------------- 346 347 protected abstract void forceStartValues(); 348 protected abstract void forceEndValues(); 349 350 protected abstract boolean containsTarget(Object target); 351 protected abstract boolean containsTarget(Object target, int tweenType); 352 353 // ------------------------------------------------------------------------- 354 // Protected API 355 // ------------------------------------------------------------------------- 356 357 protected void initializeOverride() { 358 } 359 360 protected void updateOverride(int step, int lastStep, boolean isIterationStep, float delta) { 361 } 362 363 protected void forceToStart() { 364 currentTime = -delay; 365 step = -1; 366 isIterationStep = false; 367 if (isReverse(0)) forceEndValues(); 368 else forceStartValues(); 369 } 370 371 protected void forceToEnd(float time) { 372 currentTime = time - getFullDuration(); 373 step = repeatCnt*2 + 1; 374 isIterationStep = false; 375 if (isReverse(repeatCnt*2)) forceStartValues(); 376 else forceEndValues(); 377 } 378 379 protected void callCallback(int type) { 380 if (callback != null && (callbackTriggers & type) > 0) callback.onEvent(type, this); 381 } 382 383 protected boolean isReverse(int step) { 384 return isYoyo && Math.abs(step%4) == 2; 385 } 386 387 protected boolean isValid(int step) { 388 return (step >= 0 && step <= repeatCnt*2) || repeatCnt < 0; 389 } 390 391 protected void killTarget(Object target) { 392 if (containsTarget(target)) kill(); 393 } 394 395 protected void killTarget(Object target, int tweenType) { 396 if (containsTarget(target, tweenType)) kill(); 397 } 398 399 // ------------------------------------------------------------------------- 400 // Update engine 401 // ------------------------------------------------------------------------- 402 403 /** 404 * Updates the tween or timeline state. <b>You may want to use a 405 * TweenManager to update objects for you.</b> 406 * 407 * Slow motion, fast motion and backward play can be easily achieved by 408 * tweaking this delta time. Multiply it by -1 to play the animation 409 * backward, or by 0.5 to play it twice slower than its normal speed. 410 * 411 * @param delta A delta time between now and the last call. 412 */ 413 public void update(float delta) { 414 if (!isStarted || isPaused || isKilled) return; 415 416 deltaTime = delta; 417 418 if (!isInitialized) { 419 initialize(); 420 } 421 422 if (isInitialized) { 423 testRelaunch(); 424 updateStep(); 425 testCompletion(); 426 } 427 428 currentTime += deltaTime; 429 deltaTime = 0; 430 } 431 432 private void initialize() { 433 if (currentTime+deltaTime >= delay) { 434 initializeOverride(); 435 isInitialized = true; 436 isIterationStep = true; 437 step = 0; 438 deltaTime -= delay-currentTime; 439 currentTime = 0; 440 callCallback(TweenCallback.BEGIN); 441 callCallback(TweenCallback.START); 442 } 443 } 444 445 private void testRelaunch() { 446 if (!isIterationStep && repeatCnt >= 0 && step < 0 && currentTime+deltaTime >= 0) { 447 assert step == -1; 448 isIterationStep = true; 449 step = 0; 450 float delta = 0-currentTime; 451 deltaTime -= delta; 452 currentTime = 0; 453 callCallback(TweenCallback.BEGIN); 454 callCallback(TweenCallback.START); 455 updateOverride(step, step-1, isIterationStep, delta); 456 457 } else if (!isIterationStep && repeatCnt >= 0 && step > repeatCnt*2 && currentTime+deltaTime < 0) { 458 assert step == repeatCnt*2 + 1; 459 isIterationStep = true; 460 step = repeatCnt*2; 461 float delta = 0-currentTime; 462 deltaTime -= delta; 463 currentTime = duration; 464 callCallback(TweenCallback.BACK_BEGIN); 465 callCallback(TweenCallback.BACK_START); 466 updateOverride(step, step+1, isIterationStep, delta); 467 } 468 } 469 470 private void updateStep() { 471 while (isValid(step)) { 472 if (!isIterationStep && currentTime+deltaTime <= 0) { 473 isIterationStep = true; 474 step -= 1; 475 476 float delta = 0-currentTime; 477 deltaTime -= delta; 478 currentTime = duration; 479 480 if (isReverse(step)) forceStartValues(); else forceEndValues(); 481 callCallback(TweenCallback.BACK_START); 482 updateOverride(step, step+1, isIterationStep, delta); 483 484 } else if (!isIterationStep && currentTime+deltaTime >= repeatDelay) { 485 isIterationStep = true; 486 step += 1; 487 488 float delta = repeatDelay-currentTime; 489 deltaTime -= delta; 490 currentTime = 0; 491 492 if (isReverse(step)) forceEndValues(); else forceStartValues(); 493 callCallback(TweenCallback.START); 494 updateOverride(step, step-1, isIterationStep, delta); 495 496 } else if (isIterationStep && currentTime+deltaTime < 0) { 497 isIterationStep = false; 498 step -= 1; 499 500 float delta = 0-currentTime; 501 deltaTime -= delta; 502 currentTime = 0; 503 504 updateOverride(step, step+1, isIterationStep, delta); 505 callCallback(TweenCallback.BACK_END); 506 507 if (step < 0 && repeatCnt >= 0) callCallback(TweenCallback.BACK_COMPLETE); 508 else currentTime = repeatDelay; 509 510 } else if (isIterationStep && currentTime+deltaTime > duration) { 511 isIterationStep = false; 512 step += 1; 513 514 float delta = duration-currentTime; 515 deltaTime -= delta; 516 currentTime = duration; 517 518 updateOverride(step, step-1, isIterationStep, delta); 519 callCallback(TweenCallback.END); 520 521 if (step > repeatCnt*2 && repeatCnt >= 0) callCallback(TweenCallback.COMPLETE); 522 currentTime = 0; 523 524 } else if (isIterationStep) { 525 float delta = deltaTime; 526 deltaTime -= delta; 527 currentTime += delta; 528 updateOverride(step, step, isIterationStep, delta); 529 break; 530 531 } else { 532 float delta = deltaTime; 533 deltaTime -= delta; 534 currentTime += delta; 535 break; 536 } 537 } 538 } 539 540 private void testCompletion() { 541 isFinished = repeatCnt >= 0 && (step > repeatCnt*2 || step < 0); 542 } 543 } 544