0
\$\begingroup\$

I've been making a game engine in lwjgl and I have been trying to get an ai to move from one point to another. I used a lerp method but the speed is dependent on the distance between the two points, where I need the speed to be constant

    public static Vector3f moveTo(Vector3f currentPos, Vector3f targetPos, float speed)
    {   
        float x = lerp(currentPos.x, targetPos.x, speed);
        float y = lerp(currentPos.y, targetPos.y, speed);
        float z = lerp(currentPos.z, targetPos.z, speed);
        
        return new Vector3f(x, y, z);
    }

I was thinking of dividing by the distance, like this:


    public static Vector3f moveTo(Vector3f currentPos, Vector3f targetPos, float speed)
    {   
        float dist = distance(currentPos, targetPos);
        float x = lerp(currentPos.x, targetPos.x, speed / dist);
        float y = lerp(currentPos.y, targetPos.y, speed / dist);
        float z = lerp(currentPos.z, targetPos.z, speed / dist);
        
        return new Vector3f(x, y, z);
    }

but the results are... weird, to say the least.

\$\endgroup\$
1
  • \$\begingroup\$ You're using an exponential ease-out lerp here, which is why you see the behaviour of slowing down on approach. \$\endgroup\$
    – DMGregory
    Oct 1 '21 at 11:34
1
\$\begingroup\$

Your question is not about lwjgl or implementation. It's about physics and math in fact.

Velocity from definition is v = s/t.
You want v = const and you know distance s = distance(target, source), hence you have
t = s/v = distance(targer,source)/velocity -- answering "what's the total time it will take for object to move from source to target?".

What does a moveTo() function do?

If it's meant to determine the position on a segment [A,B], then it has to ask "at what point in time?" and be called getPosition - not moveTo, because it deosn't tell the object to move -- it answers where would it be at given time. This should then have a signature position(source, target, speed, t).
From source and target you derive distance, speed is your constant, and then you determine your timeTotal=s/v (how long it takes to travel from A to B). Doing t/timeTotal = t / (distance(target,source)/velocity) gives you a fraction in range [0,1] that you then use, to lerp from A to B:

position(A, B, speed, t) => lerp(A,B, t/(distance(A,B)/speed))

This method returns absolute position at given time t of the movement, assuming that the object has uniform motion. It's good in scenarios, when you have a "keyframe" for A, B and want to interpolate between them (like in blender for example).

On the other hand when you want to apply motion in one tick of your physics simulation and you know, that the velocity is constant, then simply ds = v*dt (delta movement is velocity times delta_time of the physics).

moveTo(currentPos, target, speed, dt) => currentPos + normalize(target-currentPos)*speed*dt

where normalize(target-currentPos)*speed is direction vector scaled by speed, thus velocity vector.

Both methods are useful in different scenarios - first is related to Tweening, second is related to physics simulation (but doesn't generalize well in current form).

If you want more accurate movement with varying acceleration (right now your acceleration is constant and 0, cuz speed is constant), then you should check out methods such as Euler integration, Runge-Kutta integration or (prefereably) Verlet integration (because it +-conserves energy). If you want to read about them, check them out in context of games and algorithms, otherwise you will get lost in the mathematical jargon of the Wikipedia

\$\endgroup\$

You must log in to answer this question.