Counting the distance traveled horizontally

There are many similar topics (like this one), but I have found nothing about this specific part - horizontally.

I need to simulate a spending some travel points, but according to slopes angle. So, I have a normal points spending when I am traveling on a surface completely horizontal, and additional penalty spending when I am climbing on a slope. Therefore, I need to calculate it separately:

  1. Calculate the distance traveled horizontally.
  2. Calculate a penalty according to angle of the surface, which I am traveling on.

The first one is what I am interested in now. So, my approach is the following, with saving the last position:

Vector3 lastPosition;

void Update()
{
	var difference = transform.position - lastPosition;
	var horizontalProjection = Vector3.ProjectOnPlane(difference, Vector3.up);
	var traveledHorizontally = horizontalProjection.magnitude;

	lastPosition = transform.position;
}

And it seems like it works - traveledHorizontally is the actual path traveled horizontally every frame. The question is - is there any more compact and cheap calculations, or perhaps even a built-in solution?

I tried distance, but this is actually the distance in 3D space, not horizontally. And I can’t get a projection from this distance, because it is scalar.

var distance = Vector3.Distance(transform.position, lastPosition);

It seems like this distance is the magnitude of the difference between two vectors as in my approach, but I can’t get access to this vector to project it on a horizontal plane.

I tried also a magnitude of some horizontal movement acquired from the old input system, but the magnitude of the resulting vector is too big - it definitely not the actual distance traveled horizontally.

var horizontalMovement = transform.forward * Input.GetAxis("Vertical");
horizontalMovement += transform.right * Input.GetAxis("Horizontal");
var traveledHorizontally = horizontalMovement.magnitude;

Project on the plane is the technically correct answer and will correctly handle any normal.

An optimization for flatness: If you have a Vector3 that is the difference between this and last frame’s positions, just flatten it on the y and sum its magnitude:

Vector3 distanceThisFrame = currentPosition - previousPosition;

distanceThisFrame.y = 0;

totalDistanceFloat += distanceThisFrame.magnitude;
1 Like

just flatten it on the y
distanceThisFrame.y = 0;

Thank you. I just didn’t realize this. This trick works for projections to the planes of the main axes only, isn’t it?

Correct. The Vector3.Project() method is just handy matrix math to give you it in any arbitrary plane.

While running that project-on-plane method is unlikely to ever be an actual bottleneck (you would need to have a LOT of these things, and if you have a lot of them, it’s likely that something else would bottleneck you sooner than some extra math), but I do appreciate the simplicity of setting y to zero if you only care about x and z :slight_smile:

1 Like