How do I decerate in time to not overshot a target?

0 favourites
  • 4 posts
From the Asset Store
An educational game for Times Table. An easy to use template for developers to build larger games
  • Using the following code, the object often overshoots its target. I believe variable time steps are to blame. The higher the max speed, the larger the overshoot in absolute terms, though it seems to stay relatively consistent . Often the overshoot is only a pixel or two, but its obvious. I also tried snapping to target when close enough and slow enough, but to work, the within range needs to be high, and is obvious.

    Is there an established way to deal with this problem in variable time step games? Ideally, I don't want to stop early, or begin decelerating any sooner than needed, but I wish to respect maximum acceleration/deceleration rates.

    //Acceleration and deceleration are equal. 
    DistanceToStop = (Velocity/Acceleration) * velocity * 0.5;
    DirectionToTarget = sign(Target - CurrentPosition);
    AccelerationThisTick = Acceleration * dt * DirectionToTarget;
    
    //reverse acceleration direction for brakes
    if (distanceToStop >= distancetoTarget) 
    	AccelerationThisTick *= -1;
    
    //Integration (doesn't make a difference between this and Euler - the overshooting //still occurs)
    CurrentPosition += (Velocity * dt) + (AccelerationThisTick * dt * 0.5);
    Velocity += AccelerationThisTick;
    
    

    Tagged:

  • A few thoughts:

    You want the object to start slowing down when it’s a certain distance away. That distance is probably hit between the last frame and the current one. You could solve for that distance and between the frames and start applying the acceleration then. You could do something similar when the target is reached.

    Another idea could be to adjust the acceleration to compensate since it will always start slowing down a bit late.

    Simplest would be to clamp the position to stop at the target so it doesn’t overshoot.

  • After more tests I'm finding that stoppingDist=velocity*velocity/2/acceleration formula not well suited to stop on a fixed spot. You can compare it with the current distance to know when to start slowing down, but it will always be a bit late so it will overshoot the target position as you've seen. I also find it a bit annoying to have to handle the case where the velocity is away from the target.

    Anyways, here is one partial solution. For simplicity I'm just covering the case where the target is to the right of the position. I use a variable to store the state since we have to clamp the position and speed when moving. The result would be stopping at the target and discarding any remaining velocity.

    if state=normal
    -- //accelerate toward target
    -- velocity+=acceleration*dt
    -- position+=velocity*dt
    -- distance2target = distance(position, target)
    -- if distance2target < velocity*velocity/2/acceleration
    -- -- state=slowing
    if state=slowing
    -- velocity= max(0, velocity-acceleration*dt) 
    -- distance2target -= velocity*dt
    -- position -= velocity*dt
    -- if distance2target<0
    -- -- position-=distance2target
    -- -- velocity=0
    -- -- state=arrived

    In a similar fashion it could be modified so when the state is changed to slowing we readjust the position/velocity as if it started slowing at the precise time between frames. That would make it so there was less to no velocity leftover when it arrives. However care must be taken to handle stopping short, so we'd probably need to check the case where the relative velocity becomes negative.

    Ideally all that fully implemented could be reduced to something simpler. In the end it would just be a linear motion so maybe just switching to an ease might be more user friendly.

    As an alternate idea you could try the Arrival Steering Behavior. That would be nicer in the 2d case as it would gradually turn before smoothly slowing down to the target. I had one issue with that though, the original paper basically relies on a fixed timestep, so when I added dt it made things overshoot more. As a fix I added a tuning factor to reduce overshoot.

    https://www.dropbox.com/scl/fi/qoowuphapihsbvl0vg7lm/arrivalSteeringBehavior.capx?rlkey=quochzw7cd7fgp51elzqb2xx7&dl=1

    Another idea is to use a damped spring to do the motion. Pick a spring stiffness and then a damping so it's critically damped (aka no overshoot) and you're good.

    https://www.dropbox.com/scl/fi/mjw9c25v91mezo18n7foj/dampedspringArrive.capx?rlkey=9rjkwlv09i5qc8uu17k89ucip&dl=1
  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • I've run a few more tests, since last on the forums. It seems we are on the same page.

    To handle velocity pointing the wrong direction, I just calculated sign(sign(targetDirection) + sign(velocity)). You can set up the maths to use it to cancel out a portion of the equation by multiplying with it like in shaders, or you can just use it as a condition. If 0, apply acceleration in the direction of the target, no matter what. Although if the velocity is near 0, as you mentioned you can still create a overshoot situation if not handled.

Jump to:
Active Users
There are 1 visitors browsing this topic (0 users and 1 guests)