Search Unity

Continuous Collision Order

Discussion in 'Physics' started by Peez-Machine, Mar 9, 2019.

  1. Peez-Machine

    Peez-Machine

    Joined:
    Jul 30, 2013
    Posts:
    27
    The short version: when multiple collisions happen in the same frame using Continuous detection and kinematic motion, are those collisions supposed to fire their callbacks in the order that the collisions would happen?

    The longer version: Working in 2D, with Rigidbody2Ds and BoxColliders. A fast-moving bullet (call it R) has its RigidBody2D set to use Continuous collision with Kinematic simulation. There are three other objects, with BoxColliders and kinematic RigidBody2Ds. All objects are Kinematic and set to Use Full Kinematic Contacts (so they will actually fire the collision callbacks). They are NOT set as triggers.

    So we give R a large velocity to the right so that it moves through A B and C in a single frame. 99% of the time the OnCollisionEnter2D callbacks fire in order the collisions would happen so we see

    "Hit A"
    "Hit B"
    "Hit C"

    But there are some cases -- like if we move the spawn position of R juuuuust so -- where they fire in a different order. Is this a feature or a bug? Are continuous collisions supposed to fire in a certain order, or is the A-B-C order just coincidental?
     

    Attached Files:

  2. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    I think the order is undefined and probably depends on an internal quadtree.
     
    Antypodish likes this.
  3. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    I don't see how it's relevant, the order of collision is not guaranteed, you should design the code around that.
     
    Antypodish likes this.
  4. Peez-Machine

    Peez-Machine

    Joined:
    Jul 30, 2013
    Posts:
    27
    I think the reordering suggestion was to rule out the order being determined by something like their position in the hierarchy. I can confirm that it is not. I'm actually having trouble reproducing the "out of order" phenomenon outside of a particular scene, where I can reproduce it like clockwork. Right now it seems like it might only happen if the bullet ends the frame only overlapping a single collider (like it went through A and is sitting on B) but doesn't happen every time. I'll keep poking at it just as a curiosity -- after all, even IF the order is sequential, there's no telling what order a BUNCH of bullets will update in, so there's no way around using the callbacks to record collisions and the following Update/FixedUpdate to actually do something with them once you have the full picture.

    I haven't been able to find this behavior documented anywhere in the Unity or Box2D docs, so if anyone happens to know if/where it's hiding, I'd love to take a look.
     
  5. Peez-Machine

    Peez-Machine

    Joined:
    Jul 30, 2013
    Posts:
    27
    Hokay, a little extra fun here. In the actual game scene (where I can recreate the out-of-order), the bullet is being created at the position of a little ship. If that ship has a CircleCollider, I can sometimes get the out-of-order. If I REMOVE that collider, I can NOT reproduce the out-of-order. I'm not DOING anything with that circle collider (though it obviously immediately collides with the bullet), but its mere presence seems to have an effect.

    EDIT: Putting another collider between R and A is enough to sometimes get an out-of-order, even if R collides with it frames before it reaches A and the other walls.
     
    Last edited: Mar 9, 2019
    jrumps likes this.
  6. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Why are you even doing this? As I said before, you cannot depend on a specific order. You need to make your code account for this.
     
  7. Peez-Machine

    Peez-Machine

    Joined:
    Jul 30, 2013
    Posts:
    27
    ^^ Pretty much that. At this point I'm more curious as to why they DO appear in order so often, and looking through the Box2D code for TOI-island stuff didn't help me much. Is there any way at the end of the frame to tell which of these collision happened first at the end of the frame? I don't see any way to get time-of-impact info for CCD collisions.
     
    jrumps likes this.
  8. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,492
    The order is undefined. All contacts made in a single step are treated as if they happen simultaneously even though they are processed sequentially. The sequential processing order comes from a few things, one of them is the order colliders are reported from the broadphase which is a spatial binary tree which itself is driven from the order the original colliders are added and how the tree changes. In other words, undefined.

    In the simple case you posted, could you not do an orthogonal projection of the contact points onto the velocity vector to give you the fraction along the movement vector and hence the order?
     
  9. Peez-Machine

    Peez-Machine

    Joined:
    Jul 30, 2013
    Posts:
    27
    For sure, that would suffice for that case (as well as the cases I'm actually working with, I think) though I am curious to know what folks actually do about more complicated cases, since I highly doubt I'm the first person in the world to have to deal with simultaneous collisions.

    So consider the attached image. R moves from P0 to P1, let's say via MovePosition. Projecting the contacts onto the velocity vector erroneously shows B getting hit first (this one happens to be either a concave polygon or compound collider, but the same effect could be achieved with rotated box collider). I've got two possible solutions for this:

    Option A: Use R's position for each collision to figure out what fraction of the displacement R had covered when it caused each collision. The problem is that R's position during the collision doesn't seem to actually be available. By the time OnCollisionEnter2D is called, R's transform and rigidbody already show P1 as the position, and I don't see any way to back that information out from either the Collision2D or ContactPoint2D. (An exception would be if the colliders are axis-aligned boxes, as you could then approximate R's position using the collision penetration/normal and the collider extents.) It would be really great if the Collision2D said "sure here's the rigidbody that hit me, but also here's what its position would have been at the time."

    Option B:
    1) Move everything back to where it was (woof.)
    2) For each contact point, raycast in the opposite direction of the contact's relativeVelocity and keep whichever ray hits R first.

    Is this the harsh reality of faking essentially trigger-like behavior out of non-trigger kinematic bodies, or am I just missing something very obvious?
     

    Attached Files:

    Antypodish likes this.
  10. paradizIsCool

    paradizIsCool

    Joined:
    Jul 10, 2014
    Posts:
    178
    @MelvMay What about the same case with a non-kinematic body?
     
  11. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,492
    You can use Rigidbody2D.Cast (or Collider2D.Cast) which returns you all contacts encountered as RaycastHit2D, which (amongst other stuff) gives you the contact points and distance (along the casting axis). It's even sorted by that distance for you.
     
  12. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,492
    I don't think I follow specifically what you're asking so all I can say again is that contacts are dealt with as if they happen simultaneously so a single simulation step is a single event in time; the body-type has nothing to do with it.
     
  13. Peez-Machine

    Peez-Machine

    Joined:
    Jul 30, 2013
    Posts:
    27
    It's disappointing that it means effectively removing the bullets from the automatic simulation and doing a manual
    R2D.Cast and MovePosition for them each frame instead of just setting an initial velocity and using collision callbacks.

    Ideally, the only difference between a Full Kinematic Contacts collision and a regular Dynamic collision would be the RESPONSE (just a callback vs. callback and applying impulse). I don't know a ton about the internals of Box2D, but I have to imagine that this is pretty much what it's doing, which means that it probably knows the positions of the objects involved in a collision, even if it doesn't do anything in response to that collision. If that data does exist, it would be great if Unity would expose it. I know "expose more physics internals" is a common request, so I guess consider this one of those.

    Anyways, thanks for your time! The manual stuff will get me through this project.

    EDIT: I suppose I wouldn't have to use MovePosition, as FixedUpdate will happen before the movement and allow me to cast before the thing moves. I guess it's just a redundancy issue then -- manually casting the R2D and then having it ALSO do similar casts during the physics sim (which produce effectively useless collision data). I suppose I could just disable the colliders if I'm going to just manually cast them?
     
    Last edited: Mar 11, 2019
  14. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,492
    There's nothing to "expose". As I've said several times, all collisions that happen in a step do not happen in the order they contact over that time-slice; they are effectively at the same time. Bodies and their colliders are dealt with sequentially in an arbitrary order. The physics system doesn't gather all contacts, sort them by distance then process and report them in that order.

    And that is exactly what you get in the collision callbacks. Absolutely everything, There's no distance/fraction value provided, only contact points, normals, impulses etc.

    In actual fact, kinematic/kinematic/static collisions are not supported in Box2D. We changed it to support that and report those contacts so you're getting more than stock Box2D provides.

    The physics system provides the ability to perform shape queries which again is exactly what is provided but with more added such as sorting, filtering etc.

    Using casts (shape or ray) then performing movement is pretty standard for kinematic objects.
     
  15. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,492
    You are free to perform manual simulation and simulate whenever you want. If you're colliding with other kinematic/static objects then simple turn off full kinematic contacts then no contacts are produced and no work happens apart from the simple movement with its velocity. You only turn this on if you want the contacts reported between such objects. You are then free to perform standard sweep tests.
     
  16. Peez-Machine

    Peez-Machine

    Joined:
    Jul 30, 2013
    Posts:
    27
    Hmm, I guess if Box2D doesn't natively support kinematic contacts then it wouldn't have reason to a report a body's position at the collision moment, as this would always just match the body's final position in dynamic collisions. Apologies if I assumed that a b2TOIOutput (which tells us how far into a step a collision occurred, not to say that collisions are resolved in any particular order) made its way to Unity, but looking through Box2D it looks like it's just used locally within b2World::SolveTOI for continuous collisions but never surfaced. If all you can get are the b2Contacts via the collision callbacks then I guess that's what we've got.

    The major downside with using casts is that they don't account for an object's rotational velocity and the movement of other objects, all of which are accurately captured by the CCD to create contacts and fire the collision callbacks. So I guess it's a choice between right-ish collisions at known times/positions using casts vs. accurate collisions with no way to know the time of impact (or even just the rigidbody's position during the collision) via the simulation.
     
  17. Peez-Machine

    Peez-Machine

    Joined:
    Jul 30, 2013
    Posts:
    27
    Even if we only deal with a single collision in a frame, OnCollisionEnter2D is of questionable value for continuous kinematic collisions. Continuous kinematic collisions are unique in that the position of the participants when they collided is in no way related to their positions at the end of the frame (which is what is provided by the rigidbody data in a collision callback). This is the case neither for continuous dynamic collisions (they block motion so that the final positions are the positions at collision) nor discrete kinematic collisions (they only report a collision based on the final positions anyway).

    Looking at the attached figure, we see a kinematic body c colliding with some kinematic or static object via CCD. OnCollisionEnter2D gives us two halves of different pictures -- we get the penetration correction s based on the overlap and surface normal at c1, but we get the transform/rigidbody position from c2. If I wanted to wanted to, say, prevent a fast-moving character from tunneling through a floor, I would be unable to instead place them on top of the floor (at position c1 + s) because while I have the correction vector s for the collision, the only position I have is the entirely irrelevant c2.
     

    Attached Files:

  18. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,492
    Questionable value to you for this specific behaviour maybe but not for many others. There are a bunch of other use-cases but a very, very common use for this is colliders acting as triggers and knowing the contact points due to triggers not producing contact points and not supporting continuous collision detection at all. But yeah, doesn't seem to be what you want.

    I'm not sure why you're attempting to describe a situation using callbacks when we've already established that the information you require isn't available i.e. time-of-impact. Callbacks occur after the simulation has occured and is far too late to "correct" movement as you're now describing i.e. non-penetration and how to correct it after the simulation has run. For kinematic control, you want to check how to move then perform the corrected move based upon information you find in the environment. For that, you'd use shape casting. You can also use the following in some circumstances to calculate how to separate/move-to stuff: https://docs.unity3d.com/ScriptReference/Physics2D.Distance.html

    In theory it would be possible to add an option or more likely an overload to the Rigidbody2D.Cast query to specify you want to check for hits using its existing linear/angular velocity over a specified time rather than a specified distance. In this case, it would be able to use the integrated position/rotations for the sweep test. Right now though, that query isn't available.

    Whilst I can appreciate you're continuing to describe why you cannot use callbacks, I'm not sure how it helps you. Full kinematic contacts and continuous collision detection is of no use to you here. If none of what I've said is good for you though then there's nothing else I can suggest to get you moving forward unfortunately.
     
    Last edited: Mar 15, 2019