libgdx / gdx-ai

Artificial Intelligence framework for games based on libGDX or not. Features: Steering Behaviors, Formation Motion, Pathfinding, Behavior Trees and Finite State Machines
Apache License 2.0
1.2k stars 242 forks source link

Problem with Arrive steering behavior #35

Closed chrisapril closed 9 years ago

chrisapril commented 9 years ago

Hi,

i have found the following problem with the Arrive Steering Behavior:

When the steerable is approaching the target, the steeringOutput begins to "jump around". You can see this, when you look at the steeringOutput angle:

steeringOutput= [0.09993597:0.0035781164], len= 0.10000001, angle:2.0505471 steeringOutput= [0.099935964:0.003578156], len= 0.1, angle:2.05057 steeringOutput= [0.09993597:0.0035780247], len= 0.1, angle:2.0504947 steeringOutput= [0.09993595:0.0035785697], len= 0.1, angle:2.050807 steeringOutput= [0.09993608:0.0035749506], len= 0.10000001, angle:2.0487323 steeringOutput= [0.063587725:0.0023101782], len= 0.06362968, angle:2.0806732 steeringOutput= [0.09993937:0.0034819124], len= 0.10000001, angle:1.9953921 steeringOutput= [-0.07271707:-0.0016047317], len= 0.07273477, angle:181.26422 steeringOutput= [0.099956475:0.0029501538], len= 0.1, angle:1.6905588 steeringOutput= [-0.09558454:0.002485239], len= 0.09561685, angle:178.51062 steeringOutput= [0.09999966:2.6154998E-4], len= 0.1, angle:0.14985727 steeringOutput= [-0.096632786:0.02573139], len= 0.1, angle:165.08925

After some debugging, the problems seems to be in this line of code in Arrive.class:

// Acceleration tries to get to the target velocity without exceeding max acceleration
        // Notice that steering.linear and targetVelocity are the same vector
        targetVelocity.sub(owner.getLinearVelocity()).scl(1f / timeToTarget).limit(actualLimiter.getMaxLinearAcceleration());

When timeToTarget has the default value of .1f and the velocity is very small, for example [0.011008702:0.1401149], the resulting vector is to long [0.11008702:1.401149] and it gets limited to the MaxLinearAcceleration.

When i commenting out this code

.scl(1f / timeToTarget)

or setting timeToTarget to 1, everything works fine.

Is this behavior the way it should work? For what do i need the timeToTarget?

davebaol commented 9 years ago

The timeToTarget parameter is somewhat explained here: https://github.com/libgdx/gdx-ai/wiki/Steering-Behaviors#arrive

Have you tried specifying a smaller value? Something like 0.001f.

davebaol commented 9 years ago

As the wiki says, it's recommended that you experiment with the SteeringBehaviorsTest to get familiar with steering behaviors and all their parameters.

Closing this for now. Will reopen if needed.

If you need help just ask on the libgdx forum or, even better, on the irc channel.

chrisapril commented 9 years ago

Hi,

sorry for reopen, but you can easily reproduce this, by modifiying the SteeringActor class in your scene2d tests.

By adding a fixed timestep in applySteering method i can get the same results as described above:

private void applySteering (SteeringAcceleration<Vector2> steering, float time) {
        // Update position and linear velocity. Velocity is trimmed to maximum speed
        position.mulAdd(linearVelocity, time);

        linearVelocity.mulAdd(steering.linear, 1f).limit(getMaxLinearSpeed());
        System.out.println(linearVelocity.angle());

        // Update orientation and angular velocity
        if (independentFacing) {
            setRotation(getRotation() + (angularVelocity * time) * MathUtils.radiansToDegrees);
            angularVelocity += steering.angular * time;
        } else {
            // If we haven't got any velocity, then we can do nothing.
            if (!linearVelocity.isZero(getZeroLinearSpeedThreshold())) {
                float newOrientation = vectorToAngle(linearVelocity);
                angularVelocity = (newOrientation - getRotation() * MathUtils.degreesToRadians) * time; // this is superfluous if independentFacing is always true
                setRotation(newOrientation * MathUtils.radiansToDegrees);
            }
        }
    }
davebaol commented 9 years ago

Oh now I see what you mean with "jump around".

With your change if you set the time to target to a value between 0.5f and 1 that effect disappears.

You should tweak it to a proper value in your code, I guess.