1
votes

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.

enter image description here

I get the following value: enter image description here

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?

  • The script in which the distance is being checked is in the Update(), attached to the Player Object
  • The camera is not attached to the player, it is following the Player Object through this script: enter image description here
1
What I did, instead, was create a new object at the click location to signify the destination and use that object instead to calculate the distance. I'll leave this one open though in case anyone has an answer for this and somebody else is, for whatever reason, looking to do something similar.Răzvan T.

1 Answers

1
votes

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.