Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Bug Trouble with Raycast2D and Box Collider 2D

Discussion in 'Scripting' started by EOMedvis, Aug 31, 2021.

  1. EOMedvis

    EOMedvis

    Joined:
    Feb 19, 2019
    Posts:
    91
    I'm having an issue getting raycasts and Box Colliders in 2D.

    I'm trying to make an offscreen indicator that points a sprite towards the direction that a target object is off screen. I have a working script that doesn't use any raycasts, but it's not perfect so I thought trying to use raycast for a more accurate result.

    The basic Idea this:

    1) Get the target's screenspace position from its world position.
    2) Create a box collider 2D that is around the size of the screen (minus some padding) and align it with the screen.
    3) Cast a ray from the target screenspace position every frame towards the center of the Box collider (also the center of the screen) to see if I get a hit.
    4) If the target is offscreen, the raycast it fires towards the center will be blocked by the Box collider, and the hit.point will where the indicator should be placed on the screen.

    The ideas works in my head, but reality says otherwise, as I'm getting some really off results that's unexpected.

    upload_2021-8-31_0-4-41.png

    -The green dot is the target's screenspace position, which is returning correctly.
    -The green line is the ray cast from the target's screenspace position towards the center of the green box collider/center of screen.
    -The red line goes from the target screenspace position to the hit.point. Ideally, the 2 lines should overlap, with the red line ending where the green box starts.
    -The blue icon is the indicator. It isn't being rotated yet, since I commented out the script.
    -All of the vectors have been converted to 2D, with only X and Y coordinates.

    1) The hit position isn't where it should be. It should be where the green line intersects with the green box, but it's nowhere near that.

    upload_2021-8-31_0-7-45.png
    upload_2021-8-31_0-15-52.png
    (Turned off the green dot. The Blue hexagon is the on-screen icon, but it's being used incorrectly here despite the object being offscreen)

    2) The ray return hits from from 2 sides of the collider (left, and bottom), and only if its not too far away

    upload_2021-8-31_0-17-43.png
    (The black screen is the actual game screen. The red cone is the target. If the code was returning the hit correclt, there should be a blue-hexagon, instead of the blue triangle when the cone is within view).

    3) The inside of the collider registers as a hit.

    I've trying turning on Physics backface queries, I tried switching from 2D to 3D colliders and raycasts, same problem - Only 1 side of the collider detects a hit, the other doesn't. The code (below) doesn't set any raycast max distance, so it should be infinite, along with the Z/depth for the 2D raycast.

    I'm not sure what I'm doing wrong here. The logic seem sound at face value, but maybe there's something with raycasts and colliders have that I'm not aware of. Or it's something really stupid I'm overlooking?

    Here's the code:

    Code (CSharp):
    1.  
    2. private void LateUpdate()
    3.     {
    4.         if(tracking)
    5.         {
    6.             SetIndicator_V2();
    7.         }      
    8.     }
    9.  
    10. private void SetIndicator_V2() //more accurate version of SetIndicator that uses 2D raycasts.
    11.     {
    12.         targetScreenPos = Camera.main.WorldToScreenPoint(target.transform.position);
    13.         Vector2 targetScreenPos2D = new Vector2(targetScreenPos.x, targetScreenPos.y);
    14.         RaycastHit2D hit = Physics2D.Raycast(targetScreenPos2D, bounds.transform.position);
    15.  
    16.         if (drawScreenPosLine) //debug use only.
    17.         {
    18.             targetPos.transform.position = targetScreenPos2D;
    19.             Debug.DrawLine(targetScreenPos2D, bounds.transform.position, Color.green);
    20.         }
    21.  
    22.         if (hit.collider != null)
    23.         {
    24.             if (drawScreenPosLine) //debug use only.
    25.             {
    26.                 Debug.DrawLine(hit.point, targetScreenPos2D, Color.red);              
    27.             }
    28.  
    29.             indicator.GetComponent<Image>().sprite = offScreenSprite;
    30.             indicator.transform.position = hit.point;
    31.  
    32.             //var dir = new Vector2(targetScreenPos.x, targetScreenPos.y) - screenCenter;
    33.             //var angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
    34.             //indicator.transform.rotation = Quaternion.AngleAxis(angle - 90f, Vector3.forward);          
    35.         }
    36.         else
    37.         {
    38.             indicator.GetComponent<Image>().sprite = onScreenSprite;
    39.             indicator.transform.position = targetScreenPos2D;
    40.             indicator.transform.rotation = Quaternion.identity;
    41.             indicator.transform.localScale = new Vector3(1, 1, 1);
    42.         }
    43.     }
    44.  
    Any help would be much appreciated. Thanks in advance.
     
  2. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,321
    Take a look at the API docs for Physics2D.Raycast and you'll see it doesn't take two world-space positions because a ray is defined by a position, direction and optional distance.

    If you want to check a line segment (line between two world-space positions) then use Physics2D.Linecast.

    All closed shapes such as BoxCollider2Ds are "solid" (closed) so anything that touches them is counted as a hit, not just the edges. If you want that then you should use the only open shape which is the EdgeCollider2D with four edges; you'll only then detect where the edges are.

    I would say creating physics colliders just so you can check if a line intersects a box is an expensive way of doing a simple operation, especially if it's a non-rotated box. The code for checking if a line intersects a 2D box is trivial and you'll find it lots of places online. Alternately you could use the Bounds type and its IntersectRay method.
     
  3. EOMedvis

    EOMedvis

    Joined:
    Feb 19, 2019
    Posts:
    91
    Ah thank you. I'll give this a try!
     
    MelvMay likes this.
  4. EOMedvis

    EOMedvis

    Joined:
    Feb 19, 2019
    Posts:
    91
    Using Linecast and EdgeCollider2D worked perfectly. Will give the Bounds method a try later.

    upload_2021-8-31_11-15-8.png
     
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,520