OnCollisionStay2D question

i was trying to do a simple detection of when my character was inside a volume of water so i tried to use a simple OnCollisionStay2D , but it doesn’t work as i expected: it only detects the collision when the object is not moving, or not falling,( the rigidbody on my player is already set on neversleep because if not it would stop to detect the collision even if it was standing still)

    [SerializeField] Collider2D myBodyCollider;


    private void OnCollisionStay2D(Collision2D collision)
    {
        if (myBodyCollider.IsTouchingLayers(LayerMask.GetMask("Water")))

        {
            Debug.Log("A");
        }

    }

tried to put this simple code on a square object, and this was the behaviour
you can see it stops to log the A in the debug screen when the object is moving or falling, that would happen when my character tried to jump for example

the water is a simple composite collider used by the tilemap collider, but it is not the problem i already tried to substitute it with a simple big circle with a box collider in it

i would like it to detect i’m on water always while i am touching that layer, why it?s working only when the object is standing still?

i know there could be other ways to achieve this but i’m wondering why oncollisionstay is not working as i thought

While OnCollisionEnter usually works just fine for me, I experimented with OncCollisionExit and OnCollisionStay a couple years ago and found them to be a lot less reliable. I don’t use them at all.

1 Like

There are 2 things at play here that you need to understand.

The first is that, as the name says, OnCollision callbacks (Enter, Stay and Exit) work on colliders that are not triggers. OnCollisionStay (it doesn’t matter 2D or 3D) would only work if you are constantly touching the water but not getting in it.

The second thing is that the behaviour I assume would fit your purpose better is OnTriggerStay2D, which needs at least one of the colliders to be a trigger. However, and this is important and the documentation doesn’t mention it, for OnTriggerStay2D to work, the trigger and collider (or 2 triggers) need to be intersecting. If one of the triggers/colliders is wholly inside the other, OnTriggerStay2D doesn’t work.

2 Likes

Hmm I tested it just before and OnTriggerStay2D worked just fine for me with one object inside another. I suspect you’re right in that the issue is they could be using OnTrigger vs OnCollision, depending on how their tilemap collider is setup I guess. Since they can step all over the water I would assume that whole layer would have to be a trigger layer, in which case you’re right they should be using OnTriggerStay…

In any case it may just be easier to check if the collider is touching the water layer and not necessarily worry about using collision events. Maybe OnTriggerEnter.

I’ve read from a few sources that the “Stay” methods are a bit unpredictable at the best of times, even with continious + no sleep rigidbodies. In my quick demo this morning it worked fine but who knows how it would hold up in a more rigorous setting. I don’t tend to use that function much.

1 Like

Hi guys, i thank you all for your help, so i tested OnTriggerStay2D but as OuterGazer said it only works when the square is intersecting with the water like this
image
or when standing still :confused: , so the OnCollisionStay2D does only work when like “walking” on the edge of a volume? like touching a floor but not intersecting, while ontrigger also works when intersecting ?

and if i would want to have a behaviour like this with one of the trigger/collider wholly inside the other it’s not possible with these methods? maybe i could check with an Enter and Exit but it seems to me a bit over complicated for something as simple as this
i also tried to do something like this:

 [SerializeField] Collider2D myBodyCollider;


    private void Update()
    {
        inWater();
    }
private void inWater()
{
    if (myBodyCollider.IsTouchingLayers(LayerMask.GetMask("Water")))

    {
        Debug.Log("in water");
    }
    else
    {
        Debug.Log("not in water")
    }
}

and it works as intended, but i don’t know if it’s a good alternative and i feel a little confused by the fact that there is no simple way to manage when a collider is actually staying inside another

EDIT:
ok it seems that even the code i posted before doesn’t work really well, when walking it does work but when jumping in water it would randomly call “not in water”

Okay i tried it again without the tilemap collider and composite, and i discovered that it worked, so by tinkering it a bit i saw that by changing Geometry type on the composite from OUTLINE to POLYGONS made it work really good
image

After doing this i tried again with

 private void OnTriggerStay2D(Collider2D collision)
    {
        if (myBodyCollider.IsTouchingLayers(LayerMask.GetMask("Water_")))

        {
            Debug.Log("A");
        }

    }

and it works too
also tried with

private void OnTriggerEnter2D(Collider2D collision)
    {

        if (collision.gameObject.layer == LayerMask.NameToLayer("Water_"))
        {
            Debug.Log("in water");
        }
    }
   
    private void OnTriggerExit2D(Collider2D collision)
    {
        if (collision.gameObject.layer == LayerMask.NameToLayer("Water_"))
        {
            Debug.Log("not in water");
        }
    }

and it worked too ( even if this would be called 4 times because it would detect all the collider i have on my player)
so i think it’s all good i only have to choose which one to use

3 Likes

I’m glad you managed to work it out. If I’m not mistaken, changing the outline to poligons drastically increases the amount fo colliders and it can get heavy on performance when levels get too big. Anyway it will be mostly fine most probably for your purposes now. Another thing you might want to look into for similar situations for the future are Physics2D.Raycast() and/or overlap surfaces like Physics2D.OverlapCircle(). Those two methods are very useful to look for trigger/collider edges away from you so you can leave the composite to Outline if you needed.

1 Like

Thank you guys, you have been all really helpful :heart: :heart: :heart:

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.