Search Unity

Question Disabling a Child Collider within a CompoundCollider?

Discussion in 'Physics for ECS' started by Shinyclef, Mar 28, 2021.

  1. Shinyclef

    Shinyclef

    Joined:
    Nov 20, 2013
    Posts:
    505
    Hello,

    I have a collection of box colliders within a compound collider.
    I would like to temporarily disable collisions on a child.

    My thought process has something like this:

    1. Try changing the collision response on the child.
    -> However, I am reusing colliders from a pool of colliders when I create the compound, they are not unique.

    2. I could try modifying broadphase ModifiableBodyPair?
    -> That only gives me 1 Entity and RigidBody pair. Only the parent has either, so I guess this is the compound grain.

    3. I could try modifying the narrowphase ModifiableContactHeader/Points with my own calculations to determine which child was hit.
    -> I think this could work, but seems a shame to not be able to avoid the work in the first place. I will try this if I can't find a better way.

    4. What if I try option 1 again, but use unique colliders and keep a reference to them?
    -> I will have to stop using the pool, and create thousands of colliders, one at a time. It's so slow!
    -> Can we please get a Collider.CreateBatch API to allocate only once?

    I'm looking for advice on the recommended way to proceed.
    Thanks :)
     
  2. Shinyclef

    Shinyclef

    Joined:
    Nov 20, 2013
    Posts:
    505
    I'm looking at option 3 for now with an IContactsJob. However, as soon as I try to pass in a ComponentDataFromEntity<LocalToWorld> into the job struct, Unity complains:
    Code (CSharp):
    1. InvalidOperationException: The system Unity.Transforms.EndFrameParentSystem reads Unity.Transforms.LocalToWorld via (invalid reader index) but that type was not returned as a job dependency. To ensure correct behavior of other systems, the job or a dependency of it must be returned from the OnUpdate method.
    I can seem to get around this by Complete()ing the job within the callback:
    Code (CSharp):
    1. SimulationCallbacks.Callback callback = (ref ISimulation simulation, ref PhysicsWorld world, JobHandle inDeps) =>
    2.             {
    3.                 JobHandle jh = new DisableContactsJob
    4.                 {
    5.                     CollisionWorld = world.CollisionWorld,
    6.                     HeightPlaneData = heightPlaneData,
    7.                     LocalToWorldData = localToWorldData,
    8.                     ProjectileData = projectileData
    9.                 }
    10.                 .Schedule(simulation, ref world, inDeps);
    11.                
    12.                 jh.Complete();
    13.                 return jh;
    14.             };
    Is this possibly a bug? I tried combining inDeps with Dependency just in case, but no effect.
     
  3. milos85miki

    milos85miki

    Joined:
    Nov 29, 2019
    Posts:
    197
    Hi @Shinyclef , could you please try option 1 again? As far as I see, CompoundCollider.Create is making copies of input colliders, so you should be able to change Material or CollisionFilter on a child collider without affecting the original one.

    I don't think option 3 will work, changing a contact point or jacobian will hardly give "no collision" result. Not 100% sure what you wanted to do with LocalToWorld, but you should know that it should not be overwritten in a callback (must be read-only).

    Please let me know if this helps.
     
  4. Shinyclef

    Shinyclef

    Joined:
    Nov 20, 2013
    Posts:
    505
    Oh I see! Looks like I was indeed going down the wrong path here.

    Ok I think I see the path I need to take.
    - Get a CompoundCollider from the BlobAssetReference<Collider>
    - Access BlobArray.Accessor<Child> Children for the child.
    - Get a Collider from the Child.Collider pointer
    - Set CollisionType

    Thanks for setting me back on the yellow brick road :D.
     
    steveeHavok likes this.
  5. Shinyclef

    Shinyclef

    Joined:
    Nov 20, 2013
    Posts:
    505
    Ok I think I'm going to need to illustrate my full gameplay scenario and ask for advice. I have tried to be a big boy and solve it myself, but I think it's time to ask for help haha.

    I have a world that looks like this.
    upload_2021-3-30_20-17-51.png

    It is broken down into chunks, each with their own mesh.

    I need:
    - Standard collisions so I can walk on stuff with a character controller.
    - To disable collisions on some of these tiles/boxes. They will be destructable, so shooting them can destroy them. They will regenerate though so it's a temporary disable situation.
    - To generate the terrain quickly.
    - To have performant terrain.
    - To raycast against the terrain. But doing my own implementation against the terrain is acceptable, so I don't 'need' colliders on everything all the time.

    My approach so far, ignore the rendering side of things:
    - Create a pool of box colliders per thread.
    - Loop through geometry and cover as many boxes with as few colliders as possible (see fig. 1). I am ASSUMING it's wise to not use 1 collider per gameplay box/tile.
    - Set the colliders' geometry by updating boxGeometry via unsafe context
    - call CompoundCollider.Create. Then reusing the same pooled colliders for the next compound.

    - Put a big trigger around the whole chunk. When nothing is in the trigger, I remove the PhysicsCollider from the entity (stored in a component) and put it back on when something enters the trigger.
    Doing this made me feel smart because I assume it's a good optimisation. But probably it's stupid. Is it?

    fig.1. Example of collider generation to cover multiple boxes with one collider. Each colour represents one collider.
    upload_2021-3-30_20-21-53.png

    Pros:
    - It's fast, I have avoided the expense of BoxCollider.Create
    - I am minimising box collider counts by covering multiple boxes per collider (I assume good).
    - Entire chunks/compounds are removed from the simulation when their bounding trigger is empty. (is this a pro? I don't know...)

    Cons:
    - I cannot disable individual game play boxes in the compound collider, because there is no 1 to 1 relationship between game box/tile and collider. I do not want to rebuild the compound, it's too slow.

    I am now considering how to proceed.

    Thought 1:
    What if I try 1 collider per box. This would produce millions of static colliders. Is this madness, or is dots physics Sparta? I'm betting on madness.
    This would allow me to disable individual colliders by changing their CollisionResponse.
    I just doubt that many colliders is the way to go.

    Thought 2:
    A completely custom physics solution for this terrain, plus future voxel terrain?
    I hope I don't need to do custom... but is it the way to go here...?

    Thought 3:
    Each frame, remove all terrain colliders, and place them on tiles next to dynamic entities like players, or in front of things like projectiles.
    This is basically a half backed custom solution anyway lol. What a hack lol but if it works...

    Thought 4:
    @steveeHavok once mentioned using a 'custom physics shape' like the 'terrain collider', but it might be a rabbit hole... Is that something that could solve my woes potentially? I have no idea what is possible in this realm lol.

    Thought 5:
    Split the rendering chunk into smaller pieces for physics chunks, so that rebuilding compound colliders is not as long, perhaps 8x8 or 16x16 or something. It would be more colliders overall as I couldn't optimise larger pieces of terrain, and I would have to introduce child entities for it, and it would still be more computation to rebuild compounds than just disabling something somewhere, but could work ok.

    Questions:
    - Is 1 static collider per box going to destroy Build/StepPhysicsWorld performance? I will try it soon haha.
    - Is the cost of removing and adding many colliders per frame bad for unity physics, or perhaps the statefulness of havok physics?
    - By setting the CollisionReponse to none on a collider, does that take it out of the physics collisions calculations, or does it still go through at least the braodphase?
    - Can a custom physics shape help??
    - What would you do if you were me...?

    Appreciate the time taken to read this ^^.
    Cheers.

    edit: lol yes, millions of colliders is a bad idea. frame rate dies to CheckColliderIntegrity job.
     
    Last edited: Mar 30, 2021
    CristianOG and Thygrrr like this.
  6. milos85miki

    milos85miki

    Joined:
    Nov 29, 2019
    Posts:
    197
    Thanks for sharing your use case!
    Here are some answers:
    1. Having lots of bodies impacts performance, of course. However, we have a check for static bodies - if absolutely nothing changes on those entities between frames, cost for processing them is almost 0. In your case, I think it makes sense to group nearby boxes into compounds, especially if there are some non-destructible boxes.
    2. If there are any changes in static bodies they will be built from scratch, so the cost is proportionate to the number of bodies. Dynamic bodies are always built from scratch.
    3. CollisionReponse = none will leave the collider (whole body or compound's child) in the broadphase and it will react to queries, but will not collide with other bodies.
    4. Not sure, I'd go for grouping boxes into compounds first, especially non-destructible boxes (if such thing exists).
    5. Don't know how big your world is, but people with big worlds do stream in/out parts of the world that comes in/out "play" area. However, I'd focus on a smaller area first (something that is reasonable to be "play" area in your case) and make it work well. Streaming in/out can be implemented later.

    Another thing that might be useful - you can make physics ignore an entity by setting a PhysicsExclude component. That way, it will be completely discarded (won't go into broadphase).

    Good luck and please let me know if further help is needed!
     
    Shinyclef likes this.
  7. Shinyclef

    Shinyclef

    Joined:
    Nov 20, 2013
    Posts:
    505
    Are you sure? I saw checkCollidedIntegrity job in the profiler take a long time. What is checkColliderIntegrity doing?
     
  8. milos85miki

    milos85miki

    Joined:
    Nov 29, 2019
    Posts:
    197
    Ahaam, that's a non-production integrity/determinism check we're running. I think you can disable it by turning OFF Jobs->JobsDebugger or Jobs->Burst->SafetyChecks .
     
    Shinyclef likes this.
  9. gmentat

    gmentat

    Joined:
    Nov 12, 2012
    Posts:
    15
    Hello @Shinyclef, have you managed to find a solution for disabling a child collider?

    I am running into the same problem. I have a dynamic body consisting of multiple colliders from which I need to cut out a chunk. Spawning another version of the same object seems like the simplest solution, but it's ugly. I would love to find something better.
     
  10. Shinyclef

    Shinyclef

    Joined:
    Nov 20, 2013
    Posts:
    505
    Yes :). I'm copying code form my project it might not be in its most basic educational form, but hopefully this will give you the idea. Ignore the collector, do a raycast however you want, but if you're not raycasting, then you need a different way to get the ColliderKey which is something I have not looked into.

    Code (CSharp):
    1. if (collisionWorld.CastRay(raycastInput, ref collector))
    2. {
    3.     var hit = collector.ClosestHit;
    4.     collisionWorld.Bodies[hit.RigidBodyIndex].Collider.Value.GetLeaf(hit.ColliderKey, out ChildCollider childCol);
    5.     (*childCol.Collider).SetCollisionFilter(CollisionFilter.Zero); // this is where I'm disabling the child collider.
    6. ...
     
    Thygrrr likes this.