2

Code:

I have a function as follows:

bool Action::approach (img_comp &mover, sf::Vector2f start, sf::Vector2f end, int speed)
{
    //Get the change in x and y
    float delta_x = (end.x - start.x) / speed;
    float delta_y = (end.y - start.y) / speed;

    //Move the sprite
    mover.sprite.move(delta_x, delta_y);

    //Check if the sprite has met the end
    if (mover.sprite.getPosition() == end)
        return true;

    return false;
}

(Where sf::Vector2f is basically a struct with an x and y float parameter, e.g. a point on an x-y grid)

Question:

Unsurprisingly, this function has a possibility to never return true after passing the end point due to the rounding of float. What algorithm do I use in this scenario to have my sprite land exactly on the end point, regardless of rounding?

Note: Tolerance is not an answer to my question. It is merely a compromise. I want the algorithm that picks the points perfectly the first time, regardless of any rounding. If this is impossible, let me know.

James Hurley
  • 833
  • 1
  • 10
  • 33

3 Answers3

2

i think you have the answer already, because of the rounding of float you should not do mover.sprite.getPosition() == end but to see if mover.sprite.getPosition() - end is smaller then some number, lets say

 float diff = mover.sprite.getPosition() - end;

 if (diff < 0)
    diff *= -1;

 //Check if the sprite has met the end
 if (diff > 0.01)
        return true;

that way you check not if you are on the spot but if you are close enough to the spot. to improve that you can also do:

 float diff = mover.sprite.getPosition() - end;

 if (diff < 0)
    diff *= -1;

 //Check if the sprite has met the end
 if (diff > 0.01){
        mover.sprite.getPosition() = end; //this might not be the exact syntax but the idea is clear i hope
        return true;
 }
No Idea For Name
  • 11,411
  • 10
  • 42
  • 70
  • This is very helpful and, upon that edit, most likely what I will be doing, but it is not the only way, and not the way I was looking for. I would prefer an equation that lands me right on the first time, if only just for practice and the sake of knowing/being knowledgeable. – James Hurley Jul 28 '13 at 08:57
  • @JimHurley i would like all my programs to be nit and the way i want them to work, unfortunately programing isn't like that. there is other ways to do it, but this way is by far the most understandable... – No Idea For Name Jul 28 '13 at 09:07
  • I would like the perfect way, even if its not the most understandable. – James Hurley Jul 28 '13 at 09:10
  • 1
    This solution makes assumptions about direction of movement. If end is 1 and position is 10, 10-1>0.01, so its says we've hit the end when we haven't. – John3136 Jul 28 '13 at 09:13
2

Instead of a direct compare, you could do something like "if point is close to end then point = end"

/* Set TOLERANCE to whatever makes sense.
 * Could have different X and Y values too...
 */
#define TOLERANCE 0.01
sf::Vector2f &newPos = mover.sprite.getPoition();
if (abs(newPos[0] - end[0]) < TOLERANCE &&
    abs(newPos[1] - end[1]) < TOLERANCE) {
{
    mover.sprite.setPosition(end);
    result = true;
}
John3136
  • 28,809
  • 4
  • 51
  • 69
2

Your sprite can go to the left or to the right, so you should consider to use absolute difference between current position and end point.

Epsilon = 1e-9

deltaX = (end.x - start.x) / speed
deltaY = (end.y - start.y) / speed

dirX = Math.sgn(deltaX)
dirY = Math.sgn(deltaY)

mover.sprite.move(deltaX, deltaY);

crossedEndX = dirX * (mover.sprite.getPosition().x - end.x) > Epsilon
crossedEndY = dirY * (mover.sprite.getPosition().y - end.y) > Epsilon

if(crossedEndX && crossedEndY)
  return true
David
  • 1,138
  • 5
  • 15