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

How do Contact Filters work?

Discussion in '2D' started by kmart94, Sep 27, 2019.

  1. kmart94

    kmart94

    Joined:
    Sep 27, 2017
    Posts:
    11
    I am creating a ground checker script and unfortunately it isn't detecting the ground when I am using a contact filter. m_groundChecker is a collider2D.

    Code (CSharp):
    1.  private void GroundChecker()
    2.     {
    3.         m_isGrounded = false;
    4.  
    5.         Vector2 direction = Vector2.down;
    6.         int count = m_groundChecker.Cast(direction, m_contactFilter, m_collisionCheck, .5f);
    7.        
    8.         hitList.Clear();
    9.         for (int i = 0; i < count; i++)
    10.         {
    11.             hitList.Add(m_collisionCheck[i]); //will only contain list of object hits
    12.         }
    13.  
    14.         for (int i = 0; i < hitList.Count; i++) //looping through each hit to check normal and determine if grounded
    15.         {
    16.             Vector2 currentnormal = hitList[i].normal;
    17.             Vector2 currentpoint = hitList[i].point;
    18.             //int currentLayer = hitList[i].transform.gameObject.layer;
    19.  
    20.             if (currentnormal.y > minGroundNormalY)
    21.             {
    22.                 m_isGrounded = true;
    23.                 m_groundNormal = currentnormal;
    24.                 break;
    25.             }
    26.         }
    27.     }
    I attached a picture of my project settings, I am using layer "RayCastGroundCheck"
    As I understand, I should only be detecting collision with the ground, and platform layer although I'm not getting any detection at all. Please help!
     

    Attached Files:

  2. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,802
    You have `m_contactFilter` defined but where are you setting up any of its options?

    Read the docs:
    https://docs.unity3d.com/ScriptReference/ContactFilter2D.html

    You would need to call SetLayerMask on it first for it to filter your results by layer. So define a public or serialized LayerMask variable `groundLayers` and set it to your ground layers in the inspector, then pass that into `m_contactFilter.SetLayerMask(groundLayers)`
     
    cjm1345678, kmart94 and MelvMay like this.
  3. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,266
    Also, if you want to use the layer collision matrix for any query you can also use Physics2D.GetLayerCollisionMask to retrieve the collision mask for the layer the collider is on. To get the layer the collider is on use GameObject.layer (the GameObject the Collider2D is on).

    As said above, you pass this mask to ContactFilter2D.SetLayerMask.

    Pseudo code:
    Code (CSharp):
    1. void Start()
    2. {
    3.     // Set the filter for layers that collide with the GameObject layer.
    4.     var collisionMask = Physics2D.GetLayerCollisionMask(gameObject.layer);
    5.     m_contactFilter.SetLayerMask(collisionMask);
    6. }
     
    PoRtCuLLiS, LiterallyJeff and kmart94 like this.
  4. kmart94

    kmart94

    Joined:
    Sep 27, 2017
    Posts:
    11
    Sorry I forgot to include that bit, I set up my contact filter in start. Here is the code

    Code (CSharp):
    1. protected void Start()
    2.     {
    3.        m_contactFilter.SetLayerMask(LayerMask.GetMask("Default"));
    4.         m_contactFilter.useLayerMask = true;
    5.  
    6.  
    7.      
    8.     }
    Also I just realized I'm actually using the default layer not the ground check one but still I should be getting detection no?
     
  5. kmart94

    kmart94

    Joined:
    Sep 27, 2017
    Posts:
    11
    The only reason I don't want to use the same layer my character is currently on, is because I plan on switching the players layer to interact with different environmental props. This is a good suggestion though, maybe I can use this as a work around.
     
  6. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,266
    You'll obviously get things that are on the default layer. Default isn't every layer. Note you can make the ContactFilter2D a public field and you can change it in the editor inspector. This is much easier than doing it in script.
     
    kmart94 likes this.
  7. kmart94

    kmart94

    Joined:
    Sep 27, 2017
    Posts:
    11
    The thing is I'm not currently getting any detection. The object my ground checker is on is a platform which is checked on the default layer, so I should be getting detections and therefore be grounded. However, it is showing no detection at all!? I'll switch to making the contactfilter2d a public variable, as suggested, and I'll let you know if that helps
     
  8. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,266
    You can use ContactFilter2D().NoFilter() to reset it so that it's not filtering at all. If you still don't detect something then it's not that you're filtering out stuff.

    A few other minor things relate to code you've posted. When using the methods to set filter contents like SetLayerMask, you don't need to activate the option separately on as it'll be activated for you as it mentions in the docs. Also, you're using the Collider2D.Cast method then transferring the results to a list but depending on what version of Unity you're using, there's overloads for all queries that take (and reuse) generic list results so you can get the results directly in a list.
     
    kmart94 likes this.
  9. kmart94

    kmart94

    Joined:
    Sep 27, 2017
    Posts:
    11
    I used ContactFilter2D().NoFilter() right before I called m_groundChecker.Cast() and I am getting detection now. Not sure what to do now though since this means I'm just detecting everything right? In other words it is equivalent to calling Collider2D.Cast() without a contact filter at all? The whole point of the contact filter is to only detect collisions of a particular layer. So this indicates my contact filter is set up incorrectly no?

    Also side note, you've been fantastic help and I appreciate you taking the time to help me!
     
  10. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,266
    Yep, indicates you're not setting the layer-mask correctly or the object(s) you want are not on those layers. I presume you've not tried setting the contact filter to be a public field yet and setting the layer mask there.
     
    kmart94 likes this.
  11. kmart94

    kmart94

    Joined:
    Sep 27, 2017
    Posts:
    11
    You presume correctly, I haven't tried making it public yet. I guess I'm just confused because I thought the way I set it up in start was correct. Could it be that these two lines are not doing what I thought?

    Code (CSharp):
    1. m_contactFilter.SetLayerMask(LayerMask.GetMask("Default"));
    2.         m_contactFilter.useLayerMask = true;
     
  12. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,266
    It takes a few seconds to do this and just verify in the inspector that your code is doing what you think it's doing. It takes much longer to engage in a forum thread. ;) Personally I wouldn't ever have these hard-coded strings in my project, they're prone to error and if you decide to change a name then they stop working.

    But to answer your question then yes, the code above sets the filter to only allow the layer "Default" through and you don't need to explicitly set the "useLayerMask" to true either as it's set for you when you set the layer mask.
     
    kmart94 likes this.