Search Unity

Question Understanding Physics2D.BoxCast offsets - not just defaultContactOffset

Discussion in 'Physics' started by SamOld, Apr 8, 2023.

  1. SamOld

    SamOld

    Joined:
    Aug 17, 2018
    Posts:
    333
    I tried switching a
    Raycast
    to a
    BoxCast
    in my character controller, and ran into some bugs which I tracked down to
    BoxCast
    returning a shorter
    Distance
    , with a constant offset.

    I expected to find that this offset was
    defaultContactOffset
    , or double it. I ran some experiments box casting against a box collider, and found that actually the offset is given by
    0.0075 - 0.5 * defaultContactOffset
    . That was unexpected.

    Casting against a circle collider instead, I seem to get different results. In fact, whilst it undershoots against a box collider, it overshoots against a circle collider.

    Is this behaviour well understood or documented somewhere? Can anyone explain why it's given by that expression? And is this reliable across situations, versions, and devices?

    My conclusion here is that
    BoxCast
    is pretty much useless for precise collision detection due to these quirks. Is that reasonable?
     
  2. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,473
    Any kind of polygon (a box is a polygon too) uses offsets which is why circle/circle doesn't have any. The results returned are not meant to be a pure intersection test because the physics engine (Box2D) doesn't do that. It returns where it considers it touching as would a continuous collision contact.

    Raycast isn't used by the physics engine at all nor is overlap-point (for example) and those are just a pure intersection test.

    All the shape casts use this Box2D method (ignoring extra details): https://github.com/erincatto/box2d/...6230/src/collision/b2_time_of_impact.cpp#L258

    You can see a target/tolerance here:
    Code (CSharp):
    1. float totalRadius = proxyA->m_radius + proxyB->m_radius;
    2. float target = b2Max(b2_linearSlop, totalRadius - 3.0f * b2_linearSlop);
    3. float tolerance = 0.25f * b2_linearSlop;
     
  3. SamOld

    SamOld

    Joined:
    Aug 17, 2018
    Posts:
    333
    Thanks.

    Okay, so I take it that there's no clear documentation of the offsets, although obviously they can be reverse engineered from the code. Although the docs hint at this by using the word contacts, they don't make this distinction clear at all. Any casual reader of the docs would assume that a box cast would return the same distance as a ray cast, up to FP precision.

    Is there a good reason why Unity doesn't provide versions of these functions that do pure intersections? We all do the trick of using multiple ray casts to approximate a box cast, and it functions, but it's pretty inefficient and silly, not to mention bug prone against unusual geometry.

    The variable offsets depending on collider type makes compensating pretty impractical. I suppose I could do
    BoxCastNonAlloc
    to collect candidates, switch on the collider type, and reverse engineer compensation code or custom intersection code for each. But that's silly.
     
  4. SamOld

    SamOld

    Joined:
    Aug 17, 2018
    Posts:
    333
    @MelvMay as I'm sure you're aware, this forum is filled with many posts over the years from people who find these offsets frustrating. They are understandable in the actual collision code, but are usually undesirable and problematic in pure queries, which may not even be being used for collisions at all.

    Unity's 3D physics does this correctly. The 2D does not. I understand that this happens because Unity uses Box2D and this is what Box2D does, but that's an implementation detail, and not a good excuse for bad functionality in
    Physics2D
    .

    I assume Unity maintains a fork of Box2D. You could implement versions of the queries that work without offsets and provide them as overloads. Please do.

    This is also not properly documented. The only mention of this behaviour that I've found in the docs is in the
    defaultContactOffset
    page, and even that doesn't actually explain the functionality w.r.t. different query and collider types. There's nothing on any of the query methods. There are various threads over the years where you've posted saying that you'll get the documentation fixed, but it's never happened.