Issue with path movement/speed

Hi guys.

I’m back working on my tower defence game today and I’ve noticed a very strange issue. If I set the speed variable to anything other than 1, the movement breaks. If the value is lower than 1, then when it gets to the next tile, the movement stops. If the speed is larger than 1, the entity flickers on the screen very fast, while stuck on the first tile.

I feel like I’ve made a stupid mistake somewhere but I can’t seem to find it. I’ve posted the movement code below, along with an explanation


		for(Entity entity : entityList){
			if(entity.getPathIndex() < path.getLength()){
			
			Vector2 direction = new Vector2(path.getPath().get(entity.getPathIndex()).getX() - entity.getX(), path.getPath().get(entity.getPathIndex()).getY() - entity.getY());
			direction.nor();
			
			entity.setX(entity.getX() + direction.x * 1);
			entity.setY(entity.getY() + direction.y * 1);
				
			if(entity.getX() == path.getPath().get(entity.getPathIndex()).getX() && entity.getY() == path.getPath().get(entity.getPathIndex()).getY()){
					entity.setPathIndex(entity.getPathIndex() + 1);
				}
			}
   }
		

The code checks if the current entity’s path index is less than the path, and then creates a new vector that points to the direction of the next tile (which is then normalized). The movement code then takes place and the direction is multiplied by the speed. After the movement code, I check if the entity has got to the next tile, if it has, then it simply increments the path index for said entity and repeats the whole process.

It just makes sense in my head, but I can’t see what is going wrong ???

A problem you will often get with stuff like this

[quote]if(entity.getX() == path.getPath().get(entity.getPathIndex()).getX() && entity.getY() == path.getPath().get(entity.getPathIndex()).getY()){
[/quote]
is that due to floating point inaccuracy, two values can be not quite equal but instead 1E-20 off. Which can cause unexpected issues. Try instead of using the equality comparator, using a method like this:


public static boolean equalsTolerance(float f, float g) {
    return Maths.abs(f - g) < TOLERANCE;
}

Where TOLERANCE is some very small constant. Probably try it out somewhere in the order 1E-5 to start with.

Although an epsilon might solve the problem, it could also end up just masking it, so you might have better luck using an approach that avoids numerical precision issues altogether.

Also, in your current code you’re recomputing the direction vector every update as the entity moves (or so it appears). If the entity gets very close to the target position, the magnitude of the unnormalized direction vector could be small enough to cause normalization to fail. So, that’s another potential numerical issue you’ll want to avoid.

Assuming the path is known in advance and doesn’t change as the entity is moving, for any given segment in the path, you know exactly how far the entity needs to go, and in what direction. So, instead of computing the direction vector each update, you could instead do the following. At each node, compute the direction vector and distance to the next node. Then, simply move the entity along the computed direction vector until the computed distance has been met or exceeded. At that point, move the entity to the position of the next node (to correct for overshooting and numerical drift), and repeat.

Ahh I see D: at least I know now! I really appreciate the help guys

I really like your idea, Jesse. I think I’m going to look at implementing it, as surely the less calculations the better and it really doesn’t make sense to compute it over and over

I’m a little stuck on the calculated distance check, to tell the entity when to start moving to the next tile. I have now made each tile have a direction vector that points towards the next tile in the list, when the level is generated. I need some way to check when the entity actually reaches its destination so that it moves to the next tile, as it currently is stuck on moving towards the first tile and continues moving forever.

Surely i’d calculate the distance by xx + yy for each tile? And then check if the entity is at those cords?

[quote]Surely i’d calculate the distance by xx + yy for each tile? And then check if the entity is at those cords?
[/quote]
The expression you have there is the squared length rather than the length (assuming x and y are deltas), but maybe you already know that.

In general you don’t want to check if an entity is ‘at’ any particular coordinates, because that sort of check will likely be vulnerable to numerical error. Instead, keep track of how far the entity has traveled since the last node, and see if the entity has traveled ‘far enough’ to reach the next node. For example, if the next node is 10 units from the current node, the entity needs to travel at least 10 units to reach the next node. In other words, as soon as the distance traveled since the last node is >= 10, you start over at the next node.

Here’s some pseudocode to illustrate (typed into post, so no guarantee of correctness):

// Here's how you'd keep track of the total distance traveled since the
// last node ('totalDistance' is set to 0 at each node):
float distanceToTravelThisUpdate = speed * deltaTime;
entity.position += currentDirection * distanceToTravelThisUpdate;
totalDistance += distanceToTravelThisUpdate;

// Then, you check for reaching the next node like this:
if (totalDistance >= distanceFromCurrentNodeToNextNode) {
    totalDistance = 0;
    // Here's where you reset everything for the next node.
}

Ahh I see now ;D I’ll see if I can work on an implementation for it!

I noticed you multiplied the speed by the delta time but for some reason when I do this, I get stuttered movement, unless I set the speed to something like 20. Is this due to my speed variable just being too small? Or is there a bigger issue at play here.

I know why the speed is multiplied by the delta, just not sure on this issue

EDIT:

Nevermind, I figured it out. 1 * Gdx.graphics.getDeltaTime() would be very small. 50 * delta gives me 8.3, a much faster movement :slight_smile:

The only thing I can think of without seeing the code is to make sure you’re not using integer types where a real-number type (e.g. float) is needed.

If you’re still having problems with it, maybe post the relevant code here (and maybe describe in a bit more detail what you mean by stuttered movement).

[Edit: Looks like you solved this problem.]