Home | History | Annotate | Download | only in tweenengine
      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