Search Unity

  1. We are migrating the Unity Forums to Unity Discussions by the end of July. Read our announcement for more information and let us know if you have any questions.
    Dismiss Notice
  2. Dismiss Notice

Question RigidBody2D.Cast result depends on rotation

Discussion in '2D' started by DrDemencio, Feb 28, 2024.

  1. DrDemencio

    DrDemencio

    Joined:
    Sep 2, 2022
    Posts:
    83
    Hi everyone!

    I'm trying to make a kinematic object A push a dynamic object B, but A must stop if B hits a wall to prevent pushing it inside. I'm using RigidBody2D.Cast to predict this collision, but I found a strange behaviour: when object B (which it a regular 1x1 square) is not rotated and I make a cast to the left I get the expected results, but if I rotate it 90 degress it suddenly detects the floor.

    I tested it with a small sample:
    upload_2024-2-28_14-11-30.png
    This is the configuration for Floor:
    upload_2024-2-28_14-17-1.png
    And this is the configuration for Box:
    upload_2024-2-28_14-15-44.png
    And this is the contents of TestRigidbody2dCast:
    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3.  
    4. public class TestRigidbody2dCast : MonoBehaviour
    5. {
    6.     private void OnCollisionEnter2D(Collision2D collision)
    7.     {
    8.         Rigidbody2D rigidBody = GetComponent<Rigidbody2D>();
    9.         List<RaycastHit2D> hitResults = new(2);
    10.         _ = rigidBody.Cast(Vector2.left, hitResults, 0.5f);
    11.         foreach (RaycastHit2D hitResult in hitResults)
    12.             Debug.Log(hitResult.collider.gameObject.name);
    13.     }
    14. }
    When Box hits Floor, the output of the Console is none if Box has an angle of 0. But If I rotate it 90 degrees it is
    upload_2024-2-28_14-22-30.png

    Is there something wrong with my call to Rigidbody2D.Cast? I'm using Unity 2023.2.3f1.

    Thanks!
     
  2. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,699
    Where is the box when you rotate? Is it already in contact with the floor? If so, rotating the shape vertex involves extra calculations and tiny imprecisions can change the results i.e. it using 90.000001 degrees etc.

    In the end, this call goes to the shape-casting method in Box2D which is known for being slightly at odds to the actual contacts presented to the solver because the two are unrelated parts of Box2D. I'm not sure if this is the case here but it's always been a pain to deal with.
     
  3. flashframe

    flashframe

    Joined:
    Feb 10, 2015
    Posts:
    819
    You've got 2 Rigidbody2D components on the Box object. Not sure that would cause any issues, but don't imagine it's helpful.
     
  4. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,699
    No, that's not allowed. You can only add a single Rigidbody2D so it has to be an image-editing mistake. :)
     
    flashframe and DrDemencio like this.
  5. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,699
    If you have a minimal reproduction project you can send me here/DM then I can at least confirm what's causing the difference etc.
     
  6. DrDemencio

    DrDemencio

    Joined:
    Sep 2, 2022
    Posts:
    83
    You're right @MelvMay, copy-paste error :p

    I meant changing the rotation in the inspector before running the sample. The box is in mid-air.

    I guess I'm going to need a more robust check then.

    Thanks @MelvMay

    I'll post the sample.
     
  7. DrDemencio

    DrDemencio

    Joined:
    Sep 2, 2022
    Posts:
    83
    Here is the sample.
    As I said before, leaving the Box rotation to 0 causes no output in the console, while rotating it 90 degrees in the inspector before running will report hitting Floor.
     

    Attached Files:

  8. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,699
    So yes, it's imprecision here.

    What is common practice is to ensure you only get normals in the opposite direction you're casting along. In the case of casting "Vector2.left" (-1,0), you might only be interested in angles between -89 to +89 degrees i.e. up/slightly-right and down/slightly-right.

    You can filter collision-normal inside/outside angle ranges by specifying a ContactFilter2D in any physics query.

    Here's what I did to your test script:
    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3.  
    4. public class TestRigidbody2dCast : MonoBehaviour
    5. {
    6.     public ContactFilter2D Filter;
    7.  
    8.     private void OnCollisionEnter2D(Collision2D collision)
    9.     {
    10.         Rigidbody2D rigidBody = GetComponent<Rigidbody2D>();
    11.         List<RaycastHit2D> hitResults = new(2);
    12.         _ = rigidBody.Cast(Vector2.left, Filter, hitResults, 0.5f);
    13.         foreach (RaycastHit2D hitResult in hitResults)
    14.             Debug.Log(hitResult.collider.gameObject.name);
    15.     }
    16. }
    Of course you can automate this setting too with a helper method rather than specifying it in the inspector.

    Here's the settings:
    ContactFilterSettings.png

    It detects nothing now but if I place an "Obstruction" GameObject then it always detects it:
    Test.png


    Hope this helps.
     
    DrDemencio likes this.
  9. DrDemencio

    DrDemencio

    Joined:
    Sep 2, 2022
    Posts:
    83
    That sounds promising! Thanks a lot for your suggestion @MelvMay :)

    EDIT: I can confirm this solution fixes my problem. Thanks again!
     
    Last edited: Feb 28, 2024
    MelvMay likes this.
  10. DrDemencio

    DrDemencio

    Joined:
    Sep 2, 2022
    Posts:
    83
    I think I'm completely misunderstanding how normal filtering works.

    What's the normal angle relative to? Should the result of Vector2.Angle(hitResult.normal, Vector2.right) be between minNormalAngle and maxNormalAngle? Should their values be between -180 and 180?
     
  11. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,699
    Right (1,0) = 0
    Up (0,1) = +90 or -270
    Left (-1,0) = +180 or -180
    Down (0,-1) = +270 or -90

    Their values are not constrained. You can give it a range and select the inside or outside angle of that. Just remember that collision normals point away from surfaces hit.
     
    DrDemencio likes this.
  12. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,699
    For instance, 10 degree arc around UP would be 85 to 95.
     
  13. DrDemencio

    DrDemencio

    Joined:
    Sep 2, 2022
    Posts:
    83
    Understood, thanks a lot @MelvMay. I think it would make sense to add this explanation to the documentation (although I can see there is an explanation in the code)

    So, if I'm not mistaken, we could compute the min and max values for any direction with this code:
    Code (CSharp):
    1. float baseNormalAngle = -Vector2.SignedAngle(movementDirection, Vector2.left);
    2. float minNormalAngle = baseNormalAngle - 89f;
    3. float maxNormalAngle = baseNormalAngle + 89f;