CollisionFilter as a separate IComponentData

I’ve been working with ECS for a couple of years now and I’ve run into several use cases where I want to change what an entity can collide with, either temporarily or permanently. It seems like something that should be relatively simple to do, but is surprisingly difficult.

For context, here is what change collision filter system might look like:

public partial struct ChangeCollisionFilterSystem : ISystem
{
    private NativeList<PhysicsCollider> _createdColliders;

    [BurstCompile]
    public void OnCreate(ref SystemState state)
    {
        state.RequireForUpdate<MyEntity>();

        _createdColliders = new NativeList<PhysicsCollider>(Allocator.Persistent);
    }

    [BurstCompile]
    public void OnDestroy(ref SystemState state)
    {
        foreach (PhysicsCollider collider in _createdColliders)
        {
            collider.Value.Dispose();
        }
        _createdColliders.Dispose();
    }

    [BurstCompile]
    public void OnUpdate(ref SystemState state)
    {
        Entity entity = SystemAPI.GetSingletonEntity<MyEntity>();
        var collider = SystemAPI.GetComponent<PhysicsCollider>(entity);
        BlobAssetReference<Collider> colliderCopy = collider.Value.Value.Clone();
        CollisionFilter filter = colliderCopy.Value.GetCollisionFilter();
        filter.BelongsTo = 16;
        colliderCopy.Value.SetCollisionFilter(filter);
        PhysicsCollider newCollider = colliderCopy.AsComponent();
        _createdColliders.Add(newCollider);
        SystemAPI.SetComponent(entity, newCollider);
    }
}

This code isn’t super complex, but it highlights a couple of problem areas that new ECS users can easily run into.

  • It is not obvious that SetCollisionFilter() is going to modify ALL entities using that particular collider. This makes sense because the baking systems use a blob asset store to deduplicate colliders, but this detail is hidden from the user. If you only want to modify one collider, you have to call Clone first.
  • Calling Clone creates a new blob that must be disposed of when no longer needed. The comments for this function do call this out, but it is annoying to deal with. This problem is even more complicated when considering saving and loading the game and restoring the correct colliders.

I understand the rationale for storing colliders as blobs. Colliders are sometimes primitive shapes but can also be large meshes. Storing this data outside of a component makes a lot of sense to save on memory. It isn’t super common (at least for me) to have to change a collider shape and often there are better alternatives like swapping an entire prefab.

However, the CollisionFilter is something I’m wanting to change a lot. For example, if I want the player to pick up an object, I may want to temporarily disable collision on the object, but still be collidable with cursor raycasts. It feels like I should be able to just modify an integer on a component somewhere. But instead I have to clone the entire collider and make sure I correctly handle the lifetime of it.

Internally, a CollisionFilter only contains three integers. Even if it would increase the size of entities slightly, I feel like moving CollisionFilter to its own IComponentData (not in a blob) would reduce a lot of the complexity here. Abstractly, this also makes more sense to me. When I think of a “collider” I think of the physical shape and bounds of the object. A “collision filter” describes how that collider interacts with the world which is a separate concept from the collider itself.

If it is too late to change something like this, an alternative suggestion would be to add a PhysicsCollisionFilterOverride component added that functions similarly to the existing PhysicsMassOverride, but for override collision filters instead of changing kinematic/dynamic. This PhysicsCollisionFilterOverride would simply contain a CollisionFilter inside of it, no blob required. This component would be optional, but if present the physics system would use the override collision filter for all physics calculations.

I realize that there might be some internal complexity with both suggestions (especially considering stuff like compound colliders), but I hope that something like this is considered.

Cheers!

12 Likes

ABSOLUTELY! I’ve been calling for this as well, it makes no sense (or at least creates some major inconveniences) to not just have this a copyable data, but instead just baked hard into some opaque BlobAsset.

I assume there may be some limitations with compound colliders.

3 Likes

TL;DR: Work in Progress!

Hey all, just wanted to share that we’re hearing this loud and clear, there are some intricacies about collider and BlobAsset interactions we need to work out so that we can split out CollisionFilters and other related data components. In the most transparent manner, I think we have the right expertise on the team to figure out a solution, so do look forward to it! @daniel-holz @schembri-unity

8 Likes

Thanks for the response @IsaacsUnity !

1 Like

@scottjdaley : Hi! Just wanted to circle back to you here on this topic.

We are currently discussing this issue and one thing that came up was that there is already a feature that would allow you to modify baked colliders without cloning them. This is the “force unique” feature that ensures that the collider blob assets are not shared.
It is currently only exposed via custom physics authoring (“Force Unique” property of PhysicsShapeAuthoring) but we could provide a way to enforce this also for user-selected built-in colliders (e.g., with an extra ForceUniqueCollider authoring component that the user can add to their GameObject).

I do realize that this means that collider blob sharing is deactivated for these colliders and that this can incur a potentially large increase in memory usage, but I still wanted to point it out in case you weren’t aware of this option.

Would that be an option in your case? Or do you think loosing the ability to share memory would result in a too high memory overhead if you were to enable this for all the colliders that could potentially require runtime filter modifications?

1 Like

Another thing I wanted to mention is that while it is a great idea to move the CollisionFilter out of the collider blob into a component, it is unfortunately not easily possible to do that, for the simple reason that this would be incompatible with how compound colliders are structured at the moment.

Since every child in a compound collider can have their own collision filter (and material for that matter), the data needs to live within the compound collider blob right now and be associated with their children. Eliminating this information from the collider blobs of the children would allow only a single top-level collision filter for the entire compound collider (within the PhysicsCollider component), which is not desirable since it would prevent people from using different filters for the children of a compound collider.

We would likely need to redesign things here a bit more, e.g., via a clear split between what should go into shareable blobs (e.g., geometry only) and what should be provided as easily run-time modifiable components(e.g., collision filter and material). With that split in mind we could then build out collider functionality on the ECS component level rather than only inside the lower-level Collider blobs.

1 Like

I am having the same issues as @scottjdaley . In my case I think this would be a ok option for me.

I have found blobs to be very hard to work with. They really don’t interface well with ECS code.

Forcing compound colliders is also very inflexible.

2 Likes

I understand. We are evaluating this data layout right now.

Regarding compound colliders, when you say “forcing” do you mean that they get automatically created and this is opaque to the user, meaning the user doesn’t know about it unless they understand the particularities of how this performance optimization works?
Would you prefer an opt-in approach here rather than this automatic choice made by the baking systems?

1 Like

I would like away to be able control how compound collider are baked. Even allow for multiple compound colliders. Or keep some parts separated. So that they can be affected by hierarchical transforms and other entity level transformations.

Not all of my entities need to be super optimized. Units and characters can be heavy weight.

2 Likes

Thanks for the detailed response!

I was aware of Force Unique, but I don’t think it would be the best option for my current project. I’m making an automation game that features hundreds of identical objects and I occasionally want to temporarily edit the collision filter of a single object. Like you mentioned this would have a dramatic effect on the memory usage since every objects needs to duplicate the static collider geometry (although I mostly just use sphere and box colliders so it might not be too bad).

Yeah, I totally understand that this would be a complicated change, especially considering compound colliders. Personally, I don’t use compound colliders so its unfortunate that the API has to be more complicated to support them. I’m assuming that storing the child compound collider information in the blob is an optimization to avoid a lot of random data lookups, but this feels like an optimization that comes at the cost of usability. Perhaps the CollisionFilter and PhysicsMaterial are separate from the Collider by default, but there is some kind of opt-in optimization to bake it all into a blob for when things are static (and advanced users would know how to clone blobs properly).

Also, I’m curious what you thought of the PhysicsCollisionFilterOverride idea I mentioned in my original post. This would be a way to override the collision filter temporarily using a non-blob component. Just like the existing
PhysicsMassOverride but for collision filters. I’m not sure how PhysicsMassOverride works with compound colliders, but I’d personally be fine with overriding the entire hierarchy. This idea seems like a simple way to address this problem in the short term before redesigning the entire collider workflow.

I would definitely prefer an opt in approach for compound collider baking! Or at least give us a setting in the PhysicsShape authoring to control this. I managed to adjust my design to not need this anymore, but I previously had to resort to some baking hacks to prevent a hierarchy from being turned into a compound collider. Although I’m not actually sure its possible with the latest version of physics baking. If I understand correctly, this automatic compound collider was because child rigid bodies don’t play nicely with physics so flattening and baking to a compound collider made sense. But for cases where we’re not using rigidbodies, just colliders, it seems like we should be able to have child colliders. And from my own testing, it works fine to create (non rigidbody) collider hierarchies at runtime, but for some reason we can’t bake hierarchies this way.

Somewhat related to this discussion, but I would also like to have some way to temporarily turn off the rigidbody of a physics object. For example, I want the character to be able to pickup a loose item on the ground. Doing so would disable the rigid body, but keep the collider active. My workaround thus far has been to add and remove the PhysicsVelocity component, but this requires a structural change. Modifying PhysicsWorldIndex to be something other than 0 doesn’t work because that also disables the collider. I’d love to see an IEnableableComponent that lets me toggle the rigidbody of a physics object so that I could avoid structural changes. The structural change for my particular use case isn’t a huge deal since it isn’t happening every frame, but it seems like it would be a nice addition to the API.

Thanks again for listening to my feedback and discussing it!

2 Likes

Yes I think having a single rigidbody makes sense. Having a single compound collider on the other hand really affects usability.

I also want to be able to do this for when passengers enter vehicles.

1 Like

@TheOtherMonarch , @scottjdaley : Thanks to both of you for the added context. This is very helpful.

Just real quick on the topic of temporarily “disabling” a dynamic rigid body, which both of you are interested in. You can achieve this without a structural change by turning it into a kinematic body.
Here is how:
9064588--1253728--upload_2023-6-7_16-41-27.png

Modifying the PhysicsMass component on your dynamic rigid body this way gets the job done.
Unfortunately that would require you to cache the previous PhysicsMass component but that is manageable since it sounds like what you are doing is already a stateful operation.

2 Likes

@scottjdaley : Regarding the PhysicsCollisionFilterOverride idea, we do agree that this is the right direction but unfortunately with the compound collider situation it would make things half cooked. With this override you would essentially only be able to affect the entire compound and not its children, which, given the fact that currently the colliders are put into compounds in various scenarios, (static collider tree, rigid body that has multiple children etc.) could make this override feature both limiting and confusing.

To make a link with the mass override feature: it acts only on the rigid body level (top entity level) since that’s where the mass properties are defined. So, the hierarchical nature of compounds doesn’t get in the way here, making that feature clean and complete.

We continue to investigate this issue though. So stay tuned.
In the meantime, I hope that the cloning approach and the force unique option as a workaround at least unblocks both you and @TheOtherMonarch .
Let me know if this is not the case.

3 Likes

Ah interesting, hadn’t thought of doing it that way. Although, I’m assuming there are some performance implications of a kinematic rigidbody over a collider-only entity. I still think this could work well in some cases, but my primary use case is to disable dynamic rigidbodies of items when they are conveyor belts and then re-enable them when they are loose on the ground. I still want colliders on the items for raycasting, but I don’t want to simulate them as dynamic rigidbodies. Since most of the items are on conveyor belts, I would prefer to not have the physics system iterating all of the items and doing kinematic calculations on all of them. The only time items need to toggle like this is when the player manually picks up or drops an item so its not happening every frame. So I think I’ll stick with adding and removing the PhysicsVelocity component for now, but it is good to know that there are some other options!

That makes sense, thanks for the explanation! I agree that it would probably be a confusing feature when considering compound colliders.

Overall, it feels like there needs to be a way to control when you want static data or when you want to keep things flexible and easily editable. In a lot of cases, the colliders and collision filters are static so baking it all into a blob is the best option. But other times you want full flexibility and having it in raw non-blob components would be preferable. Even in the compound collider case, perhaps you would rather keep things in separate components on separate entities for flexibility even if that meant the physics system has to do a bunch of lookups for that child data. I think the same could be said for colliders. While mesh colliders probably only make sense as blobs, it would be nice to have easily editable colliders for the other collider shapes. AFAIK, the built-in non-ECS physics colliders support runtime editing of the colliders, such as changing the radius of a sphere collider. Seems like we should be able to opt in or opt out of baking all of this collider data into the blob when that is desirable.

I’m glad to see that this is being looked into and I’m eager to see what you come up with! Thanks!

Setting the values like you say and using a fixed joint is an option. I envisioned parenting entities rather than using a fixed joint. Parenting is what I did in my original MonoBehaviour code.

When I make my characters into passengers I also need to remove or disable their capsule collider but leave their damage hit boxers. They are already kinematic bodies.

Just my two cents here: if I’m not mistaken, the impact of having moving bodies is almost negligible. Creating collision contacts, which is the most computationally expensive operation in simulation, is worst-case O(n^2) (when all bodies are stacked in the same location), while dynamics is only O(n). The best approach you could take is to set the collider’s RespondsToCollision property to false. This way, they will still be available for queries but won’t contribute to collision calculations.

Most everything related to collisions is fully data parallel which is not the case for calculating the dynamics. The dynamics complexity in an iterative solver (which we are using) is O(k * m) = O(m), with k being the number of iterations and m the number of contacts and joints, but with the type of solver we use here, which has decent convergence qualities, we have data dependencies which need to be detected and then resolved by solving in parallel phases, reducing the parallelism that we can obtain. The ParallelSolverJobs are launched in phases for this reason, and then we also need to produce these phases which also eats up some additional computational time.

On the collision side, after in the broadphase you identified the potentially colliding pairs of colliders (which can be done as fast as in O(n) for n colliders with worst case complexity of O(n log n)), in the narrowphase you are presented with the data parallel problem of computing the detailed intersection information (contacts) for each of these pairs.
This can be done very fast in parallel and doesn’t require any iterations.
So assuming that out of your n colliders you have m << n^2 pairs that are potentially overlapping (not the extreme case you pointed out of n^2 colliding pairs, which could be considered a defective situation), your time complexity ends up being O(n) + O(m) with both phases greatly benefiting from parallelization (even the first one).

If you have a situation without any joints and only with contacts, and assuming that most of the potentially colliding pairs identified above also lead to intersections with a maximum number of contacts k per pairs, you will have k * m = O(m) many contacts which then defines the dynamics complexity (see above).

So all in all, it depends on how connected your rigid bodies are, allowing for different levels of parallelism. In some cases the dynamics time is higher and in others the collision time.

I was referring to their particular case, meaning that removing motion component won’t noticeably change anything, but removing collisions will. I believe body with no collisions will only participate in motion integration that costs nothing, while with removed motion it will participate in contact points creation and in solver phases as well, given that there are other bodies around, like in their case:

And it doesn’t require structural changes, win-win :slight_smile:
I did forget about joints indeed, but are they that noticeable with no contacts?

This one

@daniel-holz any news on this ? same pain here (i’m making a house building system which i want change piece collision when in preview mode, unique mode is not ideal and also it’s not useful in compound situation)