Universal Tween Engine 6.2.0

Mar 15, 2012 by     21 Comments    Posted under: Libraries, Universal Tween Engine

Hello!

I updated the tween engine to 6.2.0. This release proposes some very anticipated features, such as waypoints (with bezier paths!), higher precision for timings, and a static collection of easing equation, to easily find them.

Changelog

  • + added a lot more precision to timings by changing ints to floats.
  • + added paths for tweens through the ‘waypoint()’ method, no more linear transitions!
  • + added bezier paths (this is the default if you use waypoints).
  • + added TweenPaths static collection for paths implementations.
  • + added TweenEquations static collection for easing equations.
  • + added delay() to timelines too.
  • + Aadded a completely new demo to promote the engine!
  • improved performances by removing a lot of useless computations.
  • greatly reduced the memory footprint of each tween object.
  • object pooling is not enforced everywhere.
  • [api break]┬ácallback registration was changed to reduce memory footprint.
  • ! fixed a few potential bugs with kill() method and with timelines.

Project page: http://code.google.com/p/java-universal-tween-engine/
Download page: http://code.google.com/p/java-universal-tween-engine/downloads/list

Floating precision for timings
Before the update, timings were usually represented as milliseconds in integer precision. The drawback was that you couldn’t slow the animation to a speed lower than 1 ms/update. This is now over. However, your previous codes are still compatible! Indeed, the engine is now unit-independent. This means that you can use seconds, milliseconds or any unit that suits your needs, as long as you use the same unit everywhere of course.

Bezier paths
A tween can now follow waypoints before reaching the target values. Just add some .waypoint() calls to your tween decalrations. By default, the path will be followed with a catmull-rom spline, for a smooth path, but other implementations will be available by calling the .path() method.
Note: if you want to use waypoints, you first need to call once Tween.setWaypointsLimit() at the start of your program, because by default no memory is allocated for waypoints (in order to save it as much as possible).

TweenEquations collection
The TweenEquations class lists all the available easing equations, so you don’t have to use Quad.IN, Cubic.INOUT, etc. anymore. It will make the life of new users easier.

New API for callbacks
Sorry, I broke the api a bit for the callbacks :p It was a required change however, in order to reduce a lot the memory footprint of tweens. Now, only one callback is available per tween object through the .setCallback() method. By default, the callback will be triggered at the completion of the tween, but you can set every triggers you want for this callback with the .setCallbackTriggers() call.

That’s all you need to know, the rest is internal optimization to improve the execution speed as much as possible.
Have fun :)

Oh, now that I think about it, I also completely changed the test project into a complete demo. I really recommend you to have a look at it. More information about it in the next post ;)

21 Comments + Add Comment

  • Very nice demo! :-)
    I’m already using version 6.2.0 in my libgdx project, is the source of your demo also available for inspiration?

    Johan

  • Thanks for the latest release. :)

    I have encountered a problem that seems to persist, but it’s such an ugly one that I can’t really report it properly. I might doing sth wrong. It happens from 6.0.0 to 6.2.0: The tween.kill() and timeline.kill() eventually lead to non-deterministic behavior. It’s not reproducible, and before I dive into your code in more detail, I was wondering what your opinion is on a pattern like this:

    Timeline tl = Timeline.createSequence().push(...)
    tl.kill();
    Timeline tl = Timeline.createSequence().push(...)
    tl.start()
    tweenmanager.update(...)

    i.e.: is it ok to call kill at any point? I’ve found that if I eliminate all calls to kill(), everything runs perfectly. As soon as I kill() timelines anywhere, I get strange behavior. The more kills(), the worse it gets. By strange I mean: the interpolation sometimes goes from, say 1.0, 1.0, 1.0,….,1.0, 0.0, or the tween sometimes never starts.

    I’ve seen that kill() only sets a flag, but I’m wondering if I need to make a certain call before anything else in order to maintain consistency.

    best regards!

    • It’s really stange. .kill() shouldn’t lead to any undefined behavior. Actually, if a tween/timeline is killed, its update method should return immediately. Moreover, the manager should discard it directly.


      public void update(float delta) {
      if (!isStarted || isPaused || isKilled) return;
      ...

      The library is not thread-safe, maybe it is the source of your error. Is that only due to timeline killing, or does it also happen when killing tweens?

      • I noticed that it’s not thread safe, so I already trimmed everything down to one thread, which didn’t change the behavior. Currently I can get around with pause(), which of course is not optimal, but it’s good enough for me right now.

        What I’m seeing right now is that as soon as I kill(), I end up having timelines later on (from the pool) that are already built (causing runtime exceptions) or are finished, and therefore will never start. It looks to me that initialization is omitted in some cases. (But again, your code looks perfectly sane there).

        I know this is not a very good problem description. :) I will get to the cause of this.

      • sorry, forgot to mention: I think it happens with timelines only. (However I haven’t killed tweens yet)

        • Thanks for the information, I’ll investigate the issue and see if I can reproduce it. It may be due to the new kill system (isKilled flag) that was introduced in 6.2.0.

  • Excellent library! Thanks!

    I’m using it to tween the camera position between sprites in libgdx. It works beautifully.
    One use case is tweening on fling – where there is an initial velocity.
    I want the tween to behave as if two gravitation forces are pulling it between two states.
    It is easy when the initial velocity is 0 – I just call tween to one target.
    But if the initial velocity vector is > 0, than I need some physics (decelerate/accelerate).

    1) I know that this tween engine is not a physics engine, but it seems box2d is an overkill for such a task.
    Could you spare some advice if/how it is best to implement it with this tween engine?

    2) is it possible to “pause” in mid tween? I have built an elaborate timeline to animate between states, and it’s running nicely as a whole sequence. But if a user drags the plane, I need to show the interpolation as a function of the distance between the two states, and not as a function of the time.
    Is this kind of calculation possible with the library?

    Uval

    • Interpolations are computed from equations (like Quad.IN, Bounce.OUT, etc). The result only comes from these equations. They take a param between 0 and 1 and return a result. Therefore, you have 2 choices for your first question: either (1) create your own TweenEquation accounting for speed and pass it to the ease() method of your tweens, or (2) don’t apply tweens to the position of your target, but on its acceleration or its velocity, the tween engine will work as a physics engine.

      I didn’t really understand your second question. Do you an example?

  • Thanks!

    Your suggestion no, 2 seem simplest to implement, great idea!

    Example for my second question:

    Say I have two images lying on a plane.
    The tween between them involves both a camera rotation and camera placement change.
    I can trigger the tween by pressing a “next” button – this is a simple call to start the tween.
    OR, I want to be able to drag the first picture with my finger, and in this way – move the camera slowly (in the speed of the finger pan) – to the next state. But I want to use the tween interpolation while doing so. So in mid way, the camera would be in relative position and rotation (as it would be at that point during regular uninterrupted tween).

    Another example could be a transparency change – one is fading out, another fading in – while dragging.
    (dragging back and forth without releasing would apply the tween values to both element by a function of the distance between the two pictures).

    in effect, I need a call to the tween engine to set the tween % state. (Or better, to a float 0..1 representing the same).

    If I’ll bake this into a suggested API, it would be in TimeLine.java -
    /**
    * takes a snapshot in the timeline, as it would appear in position/1 of the way through the tween
    */
    public void setPosition(float position) {..}

    • ..of course not position/1 of the tween, but rather position/(total time) of the tween

      • sorry for multiple messages, but I have another idea to make my suggested api more complete –
        look at the “setPosition(..) I proposed as “setStartPosition(..)”
        now, instead of calling “start()” on the timeline, you could just set a position, and change it whenever you want to any “mid tween”.
        When/if eventually “start() would be called – it will play the tween from that point on (forward or in reverse order, as it was set).

    • I often thank about a setProgress() method. I guess I’ll implement it in BaseTween.java for the next revision. It’s just a matter of doing an update between the current time and the desired time (however, current time is not exposed to the user, so you cannot fake this function for now).

      Now, if you want to interpolate dynamically as you drag an image for instance, the Tween Engine does not officially support that, but there is a way to fake it with it. Actually, what you really need is a linear interpolation (“lerp”) and not a tween. Indeed, you only want to interpolate between two values for one single frame, and in the next frame you’ll want to interpolate between two other values, since the start and end values are now different. If you want to use the Tween Engine, one way to do that is to create a new tween on each frame, and of course kill the previous one. Since tweens are pooled, there won’t be any overhead in doing that, and it will work as intended. Another thing to take into account is the initial acceleration of the equation you will choose: it has to be >0 (ie. you cannot choose any “.IN” or “.INOUT” equation, only “.OUT” ones). Indeed, since the tween will only be used for a single frame, it has to change the position of your object at least a bit, otherwise the object will stay still.

      I used that trick myself to make a camera follow the main character of my game, and it works well. Experiment with Quad.OUT, Cubic.OUT, Quart.OUT or Quint.OUT equations to see which one suits you most (“quad” will create the most lag, while “quint” will create quick responsive movements).

      • I’m afraid that I don’t fully understand the fake mechanism.
        setProgress() would be great!! (if it can also be called when not started or while paused).
        I guess I’ll wait for your next revision :)

        Thanks!

        • If you want it better explained, feel free to open a topic on the forum, I’ll be glad to include code samples :p
          I added the “setProgress()” as an enhancement request in the issue tracker to not forget it. I’ll implement it as soon as possible.

  • Hi,

    looking for some tween/easing library but in WPF .NET , i found your project very exciting
    is any chance to have a wpf port or some guide how to achieve that ?
    wow really great project

    • Some people translated the library to C#. The official port will also come with the next release. Like the java version, it will be technology independant, so it should work with WPF, XNA, WinForms, WinRT and whatever :)

      Look in the forum for the actual translations. I never had the chance to test them though.

  • Hi

    Thanks very much for the library – it’s excellent.

    Referring to “alvi’s” earlier post, I to found a portion of the demo would fail unpredictably. I’m guessing either the killAll is not thread safe, or somewhere in the code a thread conflict is occurring which results in the following failure:

    fatel signal 11 (SIGSEGV) at 0x5d5c2008 (code=1)

    The results are after the Splash Screen, the app would remain white. I also noticed, if the app was freshly installed (making a tiny update, for instance changing a sys.out message) the error occurs on almost every occasion. Once the app is installed, the failure was intermittent on subsequent launches.

    Apologies for the hack in advance, but I found delaying tweenManager.killAll() in a separate thread permanently resolves the issue, see below my updates to SplashScreen.java:

    public void dispose() {
    //tweenManager.killAll();
    //batch.dispose();
    new Thread(new KillAllDelay()).start();
    }

    private class KillAllDelay implements Runnable {
    @Override
    public void run() {
    try {
    Thread.sleep(1000);
    tweenManager.killAll();
    batch.dispose();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    }

    I’m running the demo on a Android Samsung S3 device.

    I also notice the following non fatal issue. Within the MainActivity we call:

    requestWindowFeature(Window.FEATURE_NO_TITLE);

    However this must be called again inside com.badlogic.gdx.backends.android.AndroidApplication.initialize (line AndroidApplication.java:120) which results in the following error.

    06-20 18:52:16.310: I/AndroidApplication(26114): Content already displayed, cannot request FEATURE_NO_TITLE
    06-20 18:52:16.310: I/AndroidApplication(26114): android.util.AndroidRuntimeException: requestFeature() must be called before adding content
    06-20 18:52:16.310: I/AndroidApplication(26114): at com.android.internal.policy.impl.PhoneWindow.requestFeature(PhoneWindow.java:239)
    06-20 18:52:16.310: I/AndroidApplication(26114): at android.app.Activity.requestWindowFeature(Activity.java:3145)
    06-20 18:52:16.310: I/AndroidApplication(26114): at com.badlogic.gdx.backends.android.AndroidApplication.initialize(AndroidApplication.java:120)
    06-20 18:52:16.310: I/AndroidApplication(26114): at com.codelegs.bfg.MainActivity.onCreate(MainActivity.java:30)
    06-20 18:52:16.310: I/AndroidApplication(26114): at android.app.Activity.performCreate(Activity.java:4562)
    06-20 18:52:16.310: I/AndroidApplication(26114): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1053)
    06-20 18:52:16.310: I/AndroidApplication(26114): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1934)
    06-20 18:52:16.310: I/AndroidApplication(26114): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1995)
    06-20 18:52:16.310: I/AndroidApplication(26114): at android.app.ActivityThread.access$600(ActivityThread.java:128)
    06-20 18:52:16.310: I/AndroidApplication(26114): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1161)
    06-20 18:52:16.310: I/AndroidApplication(26114): at android.os.Handler.dispatchMessage(Handler.java:99)
    06-20 18:52:16.310: I/AndroidApplication(26114): at android.os.Looper.loop(Looper.java:137)
    06-20 18:52:16.310: I/AndroidApplication(26114): at android.app.ActivityThread.main(ActivityThread.java:4514)
    06-20 18:52:16.310: I/AndroidApplication(26114): at java.lang.reflect.Method.invokeNative(Native Method)
    06-20 18:52:16.310: I/AndroidApplication(26114): at java.lang.reflect.Method.invoke(Method.java:511)
    06-20 18:52:16.310: I/AndroidApplication(26114): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:993)
    06-20 18:52:16.310: I/AndroidApplication(26114): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:760)
    06-20 18:52:16.310: I/AndroidApplication(26114): at dalvik.system.NativeStart.main(Native Method)

    Once again, excellent library – thank you. If you get a chance to review and fix the killAll() issue, I’d be more than happy to test it out for you.

    Cheers

  • Hi,
    I started up LibGDX and the tween engine is not in the third party section. I downloaded the zip file but do not know how to get it into libGDX. Could you help me accomplish this?
    Thank you,
    Kable

  • i am not able to run the animations. Please tell me where i am wrong!!!!

    public class ParticleAccessor extends javax.swing.JFrame implements TweenAccessor{
    public ParticleAccessor() {
    initComponents();
    }

    private void initComponents() {

    }
    public static void main(String args[]) {
    /
    java.awt.EventQueue.invokeLater(new Runnable() {
    public void run() {
    ParticleAccessor pa = new ParticleAccessor();
    pa.setVisible(true);
    TweenManager m = new TweenManager();
    Tween.registerAccessor(JLabel.class, pa);
    Tween.to(particle1, ParticleAccessor.POSITION_XY, 1.0f)
    .target(100, 200)
    .start(m);

    Tween.to(particle2, ParticleAccessor.POSITION_XY, 0.5f)
    .target(0, 0)
    .ease(Bounce.OUT)
    .delay(1.0f)
    .repeatYoyo(2, 0.5f)
    .start();
    m.update(50);
    }
    });
    }
    // Variables declaration – do not modify
    private static javax.swing.JLabel particle1;
    private static javax.swing.JLabel particle2;
    // End of variables declaration

    public static final int POSITION_X = 1;
    public static final int POSITION_Y = 2;
    public static final int POSITION_XY = 3;

    // TweenAccessor implementation

    @Override
    public int getValues(JLabel target, int tweenType, float[] returnValues) {
    switch (tweenType) {
    case POSITION_X: returnValues[0] = target.getX(); return 1;
    case POSITION_Y: returnValues[0] = target.getY(); return 1;
    case POSITION_XY:
    returnValues[0] = target.getX();
    returnValues[1] = target.getY();
    return 2;
    default: assert false; return -1;
    }
    }

    @Override
    public void setValues(JLabel target, int tweenType, float[] newValues) {
    switch (tweenType) {
    case POSITION_X: target.setAlignmentX(newValues[0]); break;
    case POSITION_Y: target.setAlignmentY(newValues[0]); break;
    case POSITION_XY:
    target.setAlignmentX(newValues[0]);
    target.setAlignmentY(newValues[1]);
    break;
    default: assert false; break;
    }
    }
    }
    i am just starting to use this library. So, this code may seem silly. But please provide the correct way to do it.

  • How Can i Use this library in my java project

Got anything to say? Go ahead and leave a comment!

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>