0
\$\begingroup\$

Given a 2D-grid which is defined like below and is used to draw a grid consisting of cells:

int[,] grid; //filled with 0 (blocked) and 1 (walkable);

I also have a A*-algorithm implemented which returns a List<Vector2> with the grid cell positions of the waypoints of the path. The actual screen-positions of the cells can be calculated using the cell width. So for example a Vector2 waypoint = new Vector2(0,2); is a Cell in the first row and third column.

I also have a Player class which holds and displays a texture. When I let the Player follow the path, the texture is moving towards the top waypoint in the List. Once the waypoint is reached with position == currentgoal //comparing Vector2 here I delete it from the list and set the next waypoint as the current goal.

Here is how I update the Player position, in this case it is moving down as the direction Vector2 is set to Vector2(0,1);

float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

position += new Vector2(0,1) * speed * elapsedTime; //direction * float * float

First I was using an integer as speed and omiting the elapsedTime in the calculation. As this is not how it is supposed to be implemented I changed the speed to float and added elapsedTime. Also with an speed value of int speed = 1; the texture was moving too fast already.

Unfortunately with the float values the Vector2 comparison of position and waypoint vectors is almost never exactly reached and therefore the condition never fulfilled. This is because the goal values although being floats always hold values with a 0 after the point.

What I tried is to check for a (+/-)1 value so if the position is somewhere between currentgoal +/- 1. This does lead to a inaccuracy and other side effects.

Is my position calculation a common and right way to do it? If so, how could I modify my code which is checking whether the goal is reached or not?

\$\endgroup\$
1
\$\begingroup\$

(1) Refer to the idea of an epsilon value which represents the float error range within which you are willing to accept a "success" condition.

It is normal for floats to be compared using an epsilon, for example:

float e = 0.0001f; //epsilon

public bool FloatEquals(float a, float b)
{
    return Math.abs(a - b) <= e;
}

bool Vector2Equals(Vector2 a, Vector2 b, float e) //last param here or store as member
{
   return (a - b).magnitude <= e; //don't compare x and y individually - this ensures a is within some radius e of b
}

...thus if the absolute difference is greater than epsilon, then a and b are still too far apart to count as "equal".

(2) Alternatively, you can count millimeters, milliseconds etc. as integers rather than floats, via conversion. Then you can perform ordinary integer equality checks without any need for epsilon.

For example:

float elapsedSec = (float)gameTime.ElapsedGameTime.TotalSeconds;
int elapsedMs = elapsedSec * 1000; //this will give you integer milliseconds

Or

float xPos = ...; //some way of getting the float position
int xPosMm = xPos * 1000; //integer millimeters

With this approach you may experience some loss of accuracy over time, due to the constant rounding that occurs each game loop tick. But it simplifies equality checks.

\$\endgroup\$
  • \$\begingroup\$ Ok this looks like a nice solution, but this also would mean that I would need a custom method for comparing Vectors, right? \$\endgroup\$ – チーズパン Apr 4 '16 at 11:50
  • \$\begingroup\$ @Käsebrot Added! \$\endgroup\$ – Engineer Apr 4 '16 at 11:55
  • \$\begingroup\$ Btw, the Vector2-class does not contain a definition for magnitude in monogame. I guess Length is what you are reffering to, right? \$\endgroup\$ – チーズパン Apr 4 '16 at 12:50
  • 1
    \$\begingroup\$ @Käsebrot Correct. It's calculated as sqrt(x^2 + y^2). \$\endgroup\$ – Engineer Apr 4 '16 at 15:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.