• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
reset()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 	 */
build()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 	 */
start()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 	 */
start(TweenManager manager)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 	 */
delay(float delay)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 	 */
kill()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 	 */
free()126 	public void free() {
127 	}
128 
129 	/**
130 	 * Pauses the tween or timeline. Further update calls won't have any effect.
131 	 */
pause()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 	 */
resume()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 	 */
repeat(int count, float delay)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 	 */
repeatYoyo(int count, float delay)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 	 */
setCallback(TweenCallback callback)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 	 */
setCallbackTriggers(int flags)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 	 */
setUserData(Object data)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 	 */
getDelay()239 	public float getDelay() {
240 		return delay;
241 	}
242 
243 	/**
244 	 * Gets the duration of a single iteration.
245 	 */
getDuration()246 	public float getDuration() {
247 		return duration;
248 	}
249 
250 	/**
251 	 * Gets the number of iterations that will be played.
252 	 */
getRepeatCount()253 	public int getRepeatCount() {
254 		return repeatCnt;
255 	}
256 
257 	/**
258 	 * Gets the delay occuring between two iterations.
259 	 */
getRepeatDelay()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 	 */
getFullDuration()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 	 */
getUserData()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 	 */
getStep()292 	public int getStep() {
293 		return step;
294 	}
295 
296 	/**
297 	 * Gets the local time.
298 	 */
getCurrentTime()299 	public float getCurrentTime() {
300 		return currentTime;
301 	}
302 
303 	/**
304 	 * Returns true if the tween or timeline has been started.
305 	 */
isStarted()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 	 */
isInitialized()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 	 */
isFinished()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 	 */
isYoyo()332 	public boolean isYoyo() {
333 		return isYoyo;
334 	}
335 
336 	/**
337 	 * Returns true if the tween or timeline is currently paused.
338 	 */
isPaused()339 	public boolean isPaused() {
340 		return isPaused;
341 	}
342 
343 	// -------------------------------------------------------------------------
344 	// Abstract API
345 	// -------------------------------------------------------------------------
346 
forceStartValues()347 	protected abstract void forceStartValues();
forceEndValues()348 	protected abstract void forceEndValues();
349 
containsTarget(Object target)350 	protected abstract boolean containsTarget(Object target);
containsTarget(Object target, int tweenType)351 	protected abstract boolean containsTarget(Object target, int tweenType);
352 
353 	// -------------------------------------------------------------------------
354 	// Protected API
355 	// -------------------------------------------------------------------------
356 
initializeOverride()357 	protected void initializeOverride() {
358 	}
359 
updateOverride(int step, int lastStep, boolean isIterationStep, float delta)360 	protected void updateOverride(int step, int lastStep, boolean isIterationStep, float delta) {
361 	}
362 
forceToStart()363 	protected void forceToStart() {
364 		currentTime = -delay;
365 		step = -1;
366 		isIterationStep = false;
367 		if (isReverse(0)) forceEndValues();
368 		else forceStartValues();
369 	}
370 
forceToEnd(float time)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 
callCallback(int type)379 	protected void callCallback(int type) {
380 		if (callback != null && (callbackTriggers & type) > 0) callback.onEvent(type, this);
381 	}
382 
isReverse(int step)383 	protected boolean isReverse(int step) {
384 		return isYoyo && Math.abs(step%4) == 2;
385 	}
386 
isValid(int step)387 	protected boolean isValid(int step) {
388 		return (step >= 0 && step <= repeatCnt*2) || repeatCnt < 0;
389 	}
390 
killTarget(Object target)391 	protected void killTarget(Object target) {
392 		if (containsTarget(target)) kill();
393 	}
394 
killTarget(Object target, int tweenType)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 	 */
update(float delta)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 
initialize()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 
testRelaunch()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 
updateStep()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 
testCompletion()540 	private void testCompletion() {
541 		isFinished = repeatCnt >= 0 && (step > repeatCnt*2 || step < 0);
542 	}
543 }
544