| package aurelienribon.tweenengine; |
| |
| /** |
| * BaseTween is the base class of Tween and Timeline. It defines the |
| * iteration engine used to play animations for any number of times, and in |
| * any direction, at any speed. |
| * <p/> |
| * |
| * It is responsible for calling the different callbacks at the right moments, |
| * and for making sure that every callbacks are triggered, even if the update |
| * engine gets a big delta time at once. |
| * |
| * @see Tween |
| * @see Timeline |
| * @author Aurelien Ribon | http://www.aurelienribon.com/ |
| */ |
| public abstract class BaseTween<T> { |
| // General |
| private int step; |
| private int repeatCnt; |
| private boolean isIterationStep; |
| private boolean isYoyo; |
| |
| // Timings |
| protected float delay; |
| protected float duration; |
| private float repeatDelay; |
| private float currentTime; |
| private float deltaTime; |
| private boolean isStarted; // true when the object is started |
| private boolean isInitialized; // true after the delay |
| private boolean isFinished; // true when all repetitions are done |
| private boolean isKilled; // true if kill() was called |
| private boolean isPaused; // true if pause() was called |
| |
| // Misc |
| private TweenCallback callback; |
| private int callbackTriggers; |
| private Object userData; |
| |
| // Package access |
| boolean isAutoRemoveEnabled; |
| boolean isAutoStartEnabled; |
| |
| // ------------------------------------------------------------------------- |
| |
| protected void reset() { |
| step = -2; |
| repeatCnt = 0; |
| isIterationStep = isYoyo = false; |
| |
| delay = duration = repeatDelay = currentTime = deltaTime = 0; |
| isStarted = isInitialized = isFinished = isKilled = isPaused = false; |
| |
| callback = null; |
| callbackTriggers = TweenCallback.COMPLETE; |
| userData = null; |
| |
| isAutoRemoveEnabled = isAutoStartEnabled = true; |
| } |
| |
| // ------------------------------------------------------------------------- |
| // Public API |
| // ------------------------------------------------------------------------- |
| |
| /** |
| * Builds and validates the object. Only needed if you want to finalize a |
| * tween or timeline without starting it, since a call to ".start()" also |
| * calls this method. |
| * |
| * @return The current object, for chaining instructions. |
| */ |
| public T build() { |
| return (T) this; |
| } |
| |
| /** |
| * Starts or restarts the object unmanaged. You will need to take care of |
| * its life-cycle. If you want the tween to be managed for you, use a |
| * {@link TweenManager}. |
| * |
| * @return The current object, for chaining instructions. |
| */ |
| public T start() { |
| build(); |
| currentTime = 0; |
| isStarted = true; |
| return (T) this; |
| } |
| |
| /** |
| * Convenience method to add an object to a manager. Its life-cycle will be |
| * handled for you. Relax and enjoy the animation. |
| * |
| * @return The current object, for chaining instructions. |
| */ |
| public T start(TweenManager manager) { |
| manager.add(this); |
| return (T) this; |
| } |
| |
| /** |
| * Adds a delay to the tween or timeline. |
| * |
| * @param delay A duration. |
| * @return The current object, for chaining instructions. |
| */ |
| public T delay(float delay) { |
| this.delay += delay; |
| return (T) this; |
| } |
| |
| /** |
| * Kills the tween or timeline. If you are using a TweenManager, this object |
| * will be removed automatically. |
| */ |
| public void kill() { |
| isKilled = true; |
| } |
| |
| /** |
| * Stops and resets the tween or timeline, and sends it to its pool, for |
| + * later reuse. Note that if you use a {@link TweenManager}, this method |
| + * is automatically called once the animation is finished. |
| */ |
| public void free() { |
| } |
| |
| /** |
| * Pauses the tween or timeline. Further update calls won't have any effect. |
| */ |
| public void pause() { |
| isPaused = true; |
| } |
| |
| /** |
| * Resumes the tween or timeline. Has no effect is it was no already paused. |
| */ |
| public void resume() { |
| isPaused = false; |
| } |
| |
| /** |
| * Repeats the tween or timeline for a given number of times. |
| * @param count The number of repetitions. For infinite repetition, |
| * use Tween.INFINITY, or a negative number. |
| * |
| * @param delay A delay between each iteration. |
| * @return The current tween or timeline, for chaining instructions. |
| */ |
| public T repeat(int count, float delay) { |
| if (isStarted) throw new RuntimeException("You can't change the repetitions of a tween or timeline once it is started"); |
| repeatCnt = count; |
| repeatDelay = delay >= 0 ? delay : 0; |
| isYoyo = false; |
| return (T) this; |
| } |
| |
| /** |
| * Repeats the tween or timeline for a given number of times. |
| * Every two iterations, it will be played backwards. |
| * |
| * @param count The number of repetitions. For infinite repetition, |
| * use Tween.INFINITY, or '-1'. |
| * @param delay A delay before each repetition. |
| * @return The current tween or timeline, for chaining instructions. |
| */ |
| public T repeatYoyo(int count, float delay) { |
| if (isStarted) throw new RuntimeException("You can't change the repetitions of a tween or timeline once it is started"); |
| repeatCnt = count; |
| repeatDelay = delay >= 0 ? delay : 0; |
| isYoyo = true; |
| return (T) this; |
| } |
| |
| /** |
| * Sets the callback. By default, it will be fired at the completion of the |
| * tween or timeline (event COMPLETE). If you want to change this behavior |
| * and add more triggers, use the {@link setCallbackTriggers()} method. |
| * |
| * @see TweenCallback |
| */ |
| public T setCallback(TweenCallback callback) { |
| this.callback = callback; |
| return (T) this; |
| } |
| |
| /** |
| * Changes the triggers of the callback. The available triggers, listed as |
| * members of the {@link TweenCallback} interface, are: |
| * <p/> |
| * |
| * <b>BEGIN</b>: right after the delay (if any)<br/> |
| * <b>START</b>: at each iteration beginning<br/> |
| * <b>END</b>: at each iteration ending, before the repeat delay<br/> |
| * <b>COMPLETE</b>: at last END event<br/> |
| * <b>BACK_BEGIN</b>: at the beginning of the first backward iteration<br/> |
| * <b>BACK_START</b>: at each backward iteration beginning, after the repeat delay<br/> |
| * <b>BACK_END</b>: at each backward iteration ending<br/> |
| * <b>BACK_COMPLETE</b>: at last BACK_END event |
| * <p/> |
| * |
| * <pre> {@code |
| * forward : BEGIN COMPLETE |
| * forward : START END START END START END |
| * |--------------[XXXXXXXXXX]------[XXXXXXXXXX]------[XXXXXXXXXX] |
| * backward: bEND bSTART bEND bSTART bEND bSTART |
| * backward: bCOMPLETE bBEGIN |
| * }</pre> |
| * |
| * @param flags one or more triggers, separated by the '|' operator. |
| * @see TweenCallback |
| */ |
| public T setCallbackTriggers(int flags) { |
| this.callbackTriggers = flags; |
| return (T) this; |
| } |
| |
| /** |
| * Attaches an object to this tween or timeline. It can be useful in order |
| * to retrieve some data from a TweenCallback. |
| * |
| * @param data Any kind of object. |
| * @return The current tween or timeline, for chaining instructions. |
| */ |
| public T setUserData(Object data) { |
| userData = data; |
| return (T) this; |
| } |
| |
| // ------------------------------------------------------------------------- |
| // Getters |
| // ------------------------------------------------------------------------- |
| |
| /** |
| * Gets the delay of the tween or timeline. Nothing will happen before |
| * this delay. |
| */ |
| public float getDelay() { |
| return delay; |
| } |
| |
| /** |
| * Gets the duration of a single iteration. |
| */ |
| public float getDuration() { |
| return duration; |
| } |
| |
| /** |
| * Gets the number of iterations that will be played. |
| */ |
| public int getRepeatCount() { |
| return repeatCnt; |
| } |
| |
| /** |
| * Gets the delay occuring between two iterations. |
| */ |
| public float getRepeatDelay() { |
| return repeatDelay; |
| } |
| |
| /** |
| * Returns the complete duration, including initial delay and repetitions. |
| * The formula is as follows: |
| * <pre> |
| * fullDuration = delay + duration + (repeatDelay + duration) * repeatCnt |
| * </pre> |
| */ |
| public float getFullDuration() { |
| if (repeatCnt < 0) return -1; |
| return delay + duration + (repeatDelay + duration) * repeatCnt; |
| } |
| |
| /** |
| * Gets the attached data, or null if none. |
| */ |
| public Object getUserData() { |
| return userData; |
| } |
| |
| /** |
| * Gets the id of the current step. Values are as follows:<br/> |
| * <ul> |
| * <li>even numbers mean that an iteration is playing,<br/> |
| * <li>odd numbers mean that we are between two iterations,<br/> |
| * <li>-2 means that the initial delay has not ended,<br/> |
| * <li>-1 means that we are before the first iteration,<br/> |
| * <li>repeatCount*2 + 1 means that we are after the last iteration |
| */ |
| public int getStep() { |
| return step; |
| } |
| |
| /** |
| * Gets the local time. |
| */ |
| public float getCurrentTime() { |
| return currentTime; |
| } |
| |
| /** |
| * Returns true if the tween or timeline has been started. |
| */ |
| public boolean isStarted() { |
| return isStarted; |
| } |
| |
| /** |
| * Returns true if the tween or timeline has been initialized. Starting |
| * values for tweens are stored at initialization time. This initialization |
| * takes place right after the initial delay, if any. |
| */ |
| public boolean isInitialized() { |
| return isInitialized; |
| } |
| |
| /** |
| * Returns true if the tween is finished (i.e. if the tween has reached |
| * its end or has been killed). If you don't use a TweenManager, you may |
| * want to call {@link free()} to reuse the object later. |
| */ |
| public boolean isFinished() { |
| return isFinished || isKilled; |
| } |
| |
| /** |
| * Returns true if the iterations are played as yoyo. Yoyo means that |
| * every two iterations, the animation will be played backwards. |
| */ |
| public boolean isYoyo() { |
| return isYoyo; |
| } |
| |
| /** |
| * Returns true if the tween or timeline is currently paused. |
| */ |
| public boolean isPaused() { |
| return isPaused; |
| } |
| |
| // ------------------------------------------------------------------------- |
| // Abstract API |
| // ------------------------------------------------------------------------- |
| |
| protected abstract void forceStartValues(); |
| protected abstract void forceEndValues(); |
| |
| protected abstract boolean containsTarget(Object target); |
| protected abstract boolean containsTarget(Object target, int tweenType); |
| |
| // ------------------------------------------------------------------------- |
| // Protected API |
| // ------------------------------------------------------------------------- |
| |
| protected void initializeOverride() { |
| } |
| |
| protected void updateOverride(int step, int lastStep, boolean isIterationStep, float delta) { |
| } |
| |
| protected void forceToStart() { |
| currentTime = -delay; |
| step = -1; |
| isIterationStep = false; |
| if (isReverse(0)) forceEndValues(); |
| else forceStartValues(); |
| } |
| |
| protected void forceToEnd(float time) { |
| currentTime = time - getFullDuration(); |
| step = repeatCnt*2 + 1; |
| isIterationStep = false; |
| if (isReverse(repeatCnt*2)) forceStartValues(); |
| else forceEndValues(); |
| } |
| |
| protected void callCallback(int type) { |
| if (callback != null && (callbackTriggers & type) > 0) callback.onEvent(type, this); |
| } |
| |
| protected boolean isReverse(int step) { |
| return isYoyo && Math.abs(step%4) == 2; |
| } |
| |
| protected boolean isValid(int step) { |
| return (step >= 0 && step <= repeatCnt*2) || repeatCnt < 0; |
| } |
| |
| protected void killTarget(Object target) { |
| if (containsTarget(target)) kill(); |
| } |
| |
| protected void killTarget(Object target, int tweenType) { |
| if (containsTarget(target, tweenType)) kill(); |
| } |
| |
| // ------------------------------------------------------------------------- |
| // Update engine |
| // ------------------------------------------------------------------------- |
| |
| /** |
| * Updates the tween or timeline state. <b>You may want to use a |
| * TweenManager to update objects for you.</b> |
| * |
| * Slow motion, fast motion and backward play can be easily achieved by |
| * tweaking this delta time. Multiply it by -1 to play the animation |
| * backward, or by 0.5 to play it twice slower than its normal speed. |
| * |
| * @param delta A delta time between now and the last call. |
| */ |
| public void update(float delta) { |
| if (!isStarted || isPaused || isKilled) return; |
| |
| deltaTime = delta; |
| |
| if (!isInitialized) { |
| initialize(); |
| } |
| |
| if (isInitialized) { |
| testRelaunch(); |
| updateStep(); |
| testCompletion(); |
| } |
| |
| currentTime += deltaTime; |
| deltaTime = 0; |
| } |
| |
| private void initialize() { |
| if (currentTime+deltaTime >= delay) { |
| initializeOverride(); |
| isInitialized = true; |
| isIterationStep = true; |
| step = 0; |
| deltaTime -= delay-currentTime; |
| currentTime = 0; |
| callCallback(TweenCallback.BEGIN); |
| callCallback(TweenCallback.START); |
| } |
| } |
| |
| private void testRelaunch() { |
| if (!isIterationStep && repeatCnt >= 0 && step < 0 && currentTime+deltaTime >= 0) { |
| assert step == -1; |
| isIterationStep = true; |
| step = 0; |
| float delta = 0-currentTime; |
| deltaTime -= delta; |
| currentTime = 0; |
| callCallback(TweenCallback.BEGIN); |
| callCallback(TweenCallback.START); |
| updateOverride(step, step-1, isIterationStep, delta); |
| |
| } else if (!isIterationStep && repeatCnt >= 0 && step > repeatCnt*2 && currentTime+deltaTime < 0) { |
| assert step == repeatCnt*2 + 1; |
| isIterationStep = true; |
| step = repeatCnt*2; |
| float delta = 0-currentTime; |
| deltaTime -= delta; |
| currentTime = duration; |
| callCallback(TweenCallback.BACK_BEGIN); |
| callCallback(TweenCallback.BACK_START); |
| updateOverride(step, step+1, isIterationStep, delta); |
| } |
| } |
| |
| private void updateStep() { |
| while (isValid(step)) { |
| if (!isIterationStep && currentTime+deltaTime <= 0) { |
| isIterationStep = true; |
| step -= 1; |
| |
| float delta = 0-currentTime; |
| deltaTime -= delta; |
| currentTime = duration; |
| |
| if (isReverse(step)) forceStartValues(); else forceEndValues(); |
| callCallback(TweenCallback.BACK_START); |
| updateOverride(step, step+1, isIterationStep, delta); |
| |
| } else if (!isIterationStep && currentTime+deltaTime >= repeatDelay) { |
| isIterationStep = true; |
| step += 1; |
| |
| float delta = repeatDelay-currentTime; |
| deltaTime -= delta; |
| currentTime = 0; |
| |
| if (isReverse(step)) forceEndValues(); else forceStartValues(); |
| callCallback(TweenCallback.START); |
| updateOverride(step, step-1, isIterationStep, delta); |
| |
| } else if (isIterationStep && currentTime+deltaTime < 0) { |
| isIterationStep = false; |
| step -= 1; |
| |
| float delta = 0-currentTime; |
| deltaTime -= delta; |
| currentTime = 0; |
| |
| updateOverride(step, step+1, isIterationStep, delta); |
| callCallback(TweenCallback.BACK_END); |
| |
| if (step < 0 && repeatCnt >= 0) callCallback(TweenCallback.BACK_COMPLETE); |
| else currentTime = repeatDelay; |
| |
| } else if (isIterationStep && currentTime+deltaTime > duration) { |
| isIterationStep = false; |
| step += 1; |
| |
| float delta = duration-currentTime; |
| deltaTime -= delta; |
| currentTime = duration; |
| |
| updateOverride(step, step-1, isIterationStep, delta); |
| callCallback(TweenCallback.END); |
| |
| if (step > repeatCnt*2 && repeatCnt >= 0) callCallback(TweenCallback.COMPLETE); |
| currentTime = 0; |
| |
| } else if (isIterationStep) { |
| float delta = deltaTime; |
| deltaTime -= delta; |
| currentTime += delta; |
| updateOverride(step, step, isIterationStep, delta); |
| break; |
| |
| } else { |
| float delta = deltaTime; |
| deltaTime -= delta; |
| currentTime += delta; |
| break; |
| } |
| } |
| } |
| |
| private void testCompletion() { |
| isFinished = repeatCnt >= 0 && (step > repeatCnt*2 || step < 0); |
| } |
| } |