2

Not sure if libgdx offers this functionality, so it might be a maths question. I have various 3D camera coordinates stored as a Vector3, and I want to move the camera from one to the other.

Vector3 CAM_POSITION_DEFAULT = new Vector3(0f, 1f, -5f);
Vector3 CAM_POSITION_TOP = new Vector3(0f, 10f, -5f);

I could just set the camera position to the new one, but this is a but too sharp. A nice smooth transition, with some easing (slow start, fast mid, slow end) would be ideal.

Is this just a simple matrix manipulation, or is there a tidy way to get this functionality with easing? Secondly, if easing is available, is it possible to use different forms of easing (like in javascript's jQuery library)?

Many thanks.

Jammo
  • 1,838
  • 4
  • 25
  • 39
  • You might have a look at https://code.google.com/p/java-universal-tween-engine/ which is commonly used in libgdx for all kinds of interpolations/easing. – noone Apr 06 '14 at 09:39

1 Answers1

3

What you are looking for is a simple linear interpolation between the two positions.

To achieve this, you can use libgdx's provided lerp(...) method.

If you want to calculate the positions inbetween CAM_POSITION_DEFAULT and CAM_POSITION_TOP, you just need to interpolate between the two vectors:

Vector3 inbetween = CAM_POSITION_DEFAULT.lerp(CAM_POSITION_TOP, alpha);

If you provide a value of alpha=0, you will get CAM_POSITION_DEFAULT returned, if you provide alpha=1, you will get CAM_POSITION_TOP... alpha=0.5f will therefore be the point directly in the middle of the two positions...

The trick now is how to choose the alpha value for a smooth transition that takes a defined amount of seconds:

class CameraTransition {

    private final Vector3 startPos;
    private final Vector3 goalPos;
    private final float duration;

    private float stateTime = 0.0f;

    public CameraTransition(final Vector3 startPos, final Vector3 goalPos, float duration) {
        this.startPos = startPos;
        this.goalPos = goalPos;
        this.duration = duration;
    }

    public Vector3 act(float delta) {
        stateTime += delta;

        return startPos.lerp(goalPos, MathUtils.clamp(stateTime/duration, 0.0f,1.0f));
    }

}

So you could use this code (not tested, sorry)... Create a new CameraTransition and set the duration.

Then within your render(float delta) you just have to call the act() method and pass the delta time. Of course you should only call the act() method when you want the transition to run.

Hope this helps... :)

UPDATE:

After seeing that you are looking for a smooth transition which starts slowly and ends slowly, I have changed the interpolation to something that uses a smoother curve instead of linear interpolation:

class CameraTransition {

        private final Vector3 startPos;
        private final Vector3 goalPos;
        private final float duration;

        private float stateTime = 0.0f;
        private float alpha;

        public CameraTransition(final Vector3 startPos, final Vector3 goalPos, float duration) {
            this.startPos = startPos;
            this.goalPos = goalPos;
            this.duration = duration;
        }

        public Vector3 act(float delta) {
            stateTime += delta;

            alpha = MathUtils.clamp(stateTime/duration, 0.0f,1.0f);


            return startPos.lerp(goalPos, -2 * alpha*alpha*alpha + 3 * alpha*alpha);
        }

    }

In this version, I am chaning the linear interpolation to use this curve:

f(x) = -2*alpha^3 + 3*alpha^2

http://www.wolframalpha.com/input/?i=plot+f%28x%29+%3D+-2x%5E3+%2B3x%5E2+from+0+to+1

florianbaethge
  • 2,520
  • 3
  • 22
  • 29
  • 1
    This does not meet the "slow start, fast mid, slow end" condition of the OP. The speed of linear interpolation is constant. – More Axes Apr 06 '14 at 10:49
  • Ohhh sorry I must have skipped that condition while reading... Anyways, this might still be possible using (almost) the code above and use the Interpolation class http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/Interpolation.html – florianbaethge Apr 06 '14 at 11:22
  • Ok I added a fixed version... :) – florianbaethge Apr 06 '14 at 11:48
  • 1
    Thanks evident, `lerp()` was exactly what I was looking for. The interpolation between two vectors, and then I can put a bespoke easing over the top of it. Appreciate your effort. Cheers – Jammo Apr 07 '14 at 07:34
  • Quick question: When moving the camera to various positions, I always set the `lookAt(0,0,0)` to keep it focused on the one point. However, when moving the camera around, it starts losing its `up` vector and starts to "topple" – Jammo Apr 07 '14 at 09:50
  • Actually, disregard. I couldn't set the `up` vector, until I realised I can just do this instead: `this.cam.up.set(0, 1, 0);` Now works perfectly. Thanks again – Jammo Apr 07 '14 at 09:58
  • Ahh ok :) I am not sure, but you might also use camera.normalizeUp() for this: http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/graphics/Camera.html#normalizeUp() ... It should re-normalize the up vector if it has changed... – florianbaethge Apr 07 '14 at 10:04