Search Unity

Physics2DRaycaster is broken

Discussion in 'Physics' started by the_lemur, Nov 17, 2016.

  1. the_lemur

    the_lemur

    Joined:
    Apr 3, 2013
    Posts:
    104
    I've tried to bring this to someone's attention in several threads because it really comes up a lot when I try to use these raycasters. I actually know why the error occurs, and have written my own fix, but it peppers the code with annoying special cases.

    I've seen people with the same issue from years ago, and there is not even an official reply.

    It's very simple: If you have a large 2D object like a map, and smaller 2D objects like rpg-units placed over them, the Physics2DRaycaster will often report clicks on the map BENEATH the units, instead of the units which are above the map.

    THIS IS WHY:

    Physics2DRaycaster LINE 36
    https://bitbucket.org/Unity-Technol...caster.cs?at=5.2&fileviewer=file-view-default

    The 3D euclidean distance is assigned to the Hit result, causing 2D objects to be depth-sorted according to 3D coordinates.

    You cannot use the euclidean 3D distance when sorting distances to objects in 2D. You must sort according to the PROJECTION of the distance onto the camera forward vector.

    Otherwise, an object far away from the camera in the x,y axes, is seen as being very, very far away from the camera, even when it's close to the camera in the z-direction.

    Whenever an IPointer event tells me an object was clicked (the assumedHit) I have to correct for fake hits in my code:
    Code (csharp):
    1.  
    2. private GameObject CheckAltHit( PointerEventData data, Transform assumedHit ) {
    3.  
    4.        Ray ray = Camera.main.ScreenPointToRay( data.position );
    5.        RaycastHit2D[] hits = Physics2D.RaycastAll( ray.origin, ray.direction );
    6.  
    7.        Vector3 camDir = Camera.main.transform.forward;
    8.        Vector3 camPos = Camera.main.transform.position;
    9.  
    10.        float minDist = Vector3.Dot( camDir, assumedHit.position - camPos );
    11.        float temp;
    12.  
    13.        GameObject closest = null;
    14.  
    15.        foreach ( var hit in hits ) {
    16.  
    17.          if ( hit.transform == assumedHit) {
    18.            continue;
    19.          }
    20.  
    21.          temp = Vector3.Dot( hit.transform.position - camPos, Camera.main.transform.forward );
    22.          if ( temp < minDist ) {
    23.            closest = hit.transform.gameObject;
    24.            minDist = temp;
    25.          }
    26.  
    27.        } // for
    28.  
    29.        return closest;
    30.  
    31.      }
    32.  
    This fixes the error but I have to pepper these for every IPointer and IDrag event. The projection test should be done by the 2D system itself.
     
    Last edited: Nov 17, 2016
  2. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,500
    I think the UI team wrote the above method but at some point it was changed to use the correct 2D physics function.

    'Physics2D.RaycastAll' was incorrect as that is a 2D function that takes a Vector2 point and direction; it should use 'Physics2D.GetRayIntersectionAll' which does 3D projection.

    I can see inside the code that this was changed 11 months ago so that would suggest it went into at least 5.4.0.

    EDIT: Yep. https://bitbucket.org/Unity-Technol...caster.cs?at=5.4&fileviewer=file-view-default