Reputation: 360
I am having an issue with my Unity3D project. I am currently working at an isometric game, and I am unable to find a way to calculate the exact distance between the Player Object and the location that I click on.
Here is my code that gets the location of the click and calculates the distance between the Player Object and the clicked location.
ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Physics.Raycast(ray.origin, ray.direction, out hitInfo);
var testDistance = Vector3.Distance(transform.position, ray.direction.normalized);
Edit: Replacing ray.direction.normalized with hitInfo.transform.position has similar results, a value much bigger than the actual distance.
However, I have the following issue. Even when I click right under the player model, the testDistance variable is way bigger than I expected.
I am aware I am definitely missing some kind of transformation or step that has to be done.
Some more details that could help, maybe?
Upvotes: 1
Views: 1797
Reputation: 90813
Well your issue is pretty simple:
You are checking the distance between the player position and the ray direction!
Of course this makes absolutely no sense since it basically equals the Camera.main.transform.forward
. If you use a normalized direction like a position it will basically be something around the Unity origin at a distance of 1. While your player could be positioned just anywhere. You want to check the distance between player and hitInfo.point
!
// If possible already reference this in the Inspector
[SerializeField] private Camera _camera;
private void Awake ()
{
// As fallback get the camera ONCE on runtime, "Camera.main" is expensive!
//see https://docs.unity3d.com/ScriptReference/Camera-main.html
if(!_camera) _camera = Camera.main;
}
private void Update()
{
var ray = _camera.ScreenPointToRay(Input.mousePosition);
if(Physics.Raycast(ray, out hitInfo)
{
// Get the position where exactly you hit something
var hitPoint = hitInfo.point;
// Regardless of what we hit eliminate any difference in the Y axis
hitPoint.y = 0;
// also map the player position on the XZ plane (erase any Y axis height)
var playerPosition = transform.position;
playerPosition.y = 0;
// Now you get the correct distance between both points in the XZ plane
var distance = Vector3.Distance(hitPoint, playerPosition);
Debug.Log($"Distance: {distance}", this);
}
}
What I would use in your case is the intersection point of the ray and the Playboard - assuming the Unity XZ Plane
.
And then get the distance between your player position mapped onto that XZ plane and the ray hit point in the XZ plane.
The advantage of this is that this is pure mathematical and doesn require any colliders and can not break if a different collider is in the way.
Something like
// If possible already reference this in the Inspector
[SerializeField] private Camera _camera;
// First parameter is the global UP axis -> we get the flat floor in XZ axis
// For the second parameter you could e.g. change the Y component if there needs
// to be a ground height different to 0
// see https://docs.unity3d.com/ScriptReference/Plane-ctor.html
private Plane _xzPlane = new Plane(Vector3.up, Vector3.zero);
private void Awake ()
{
// As fallback get the camera ONCE on runtime, "Camera.main" is expensive!
//see https://docs.unity3d.com/ScriptReference/Camera-main.html
if(!_camera) _camera = Camera.main;
}
private void Update()
{
var ray = _camera.ScreenPointToRay(Input.mousePosition);
// Directly use a raycasts on the XZ plane -> pure mathematical doesn't need any collider/physics
// see https://docs.unity3d.com/ScriptReference/Plane.Raycast.html
if(plane.Raycast(ray, out hitDistance)
{
// get the position where we hit the XZ plane
// see https://docs.unity3d.com/ScriptReference/Ray.GetPoint.html
var xzHitPoint = ray.GetPoint(hitDistance);
// map the player position on the XZ plane (erase any Y axis height)
// see https://docs.unity3d.com/ScriptReference/Plane.ClosestPointOnPlane.html
var xzPlayerPosition = plane.ClosestPointOnPlane(transform.position);
// Now you get the correct distance between both points in the XZ plane
var distance = Vector3.Distance(xzHitPoint, xzPlayerPosition);
Debug.Log($"Distance: {distance}", this);
}
}
API links are in the code comments.
Upvotes: 1