问题
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.
回答1:
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;
}
回答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
回答3:
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;
}
来源:https://stackoverflow.com/questions/17906535/algorithm-for-movement-along-linear-line