Skip to content

Mutually exclusive components#22818

Open
eugineerd wants to merge 5 commits intobevyengine:mainfrom
eugineerd:mutually-exclusive-components
Open

Mutually exclusive components#22818
eugineerd wants to merge 5 commits intobevyengine:mainfrom
eugineerd:mutually-exclusive-components

Conversation

@eugineerd
Copy link
Contributor

Objective

Implement mutually exclusive invariant from #1481, allowing enum-like components that cannot co-exist on the same archetype.

Solution

Add World::register_mutually_exclusive_components that allows to register which components are incompatible with each other and would be removed if any of the others are inserted.

    world.register_mutually_exclusive_components::<(CompA, CompB)>();

    let e = world.spawn(CompA).id();
    world.entity_mut(e).insert(CompB);
    assert!(!world.entity(e).contains::<CompA>());
    world.entity_mut(e).insert(CompA);
    assert!(!world.entity(e).contains::<CompB>());

This is a minimal implementation which just makes this pattern integrated with the ecs - World::register_mutually_exclusive_components is the only public interface featured in this PR.

Possible extensions that are left to future PRs:

  • #[derive(Component)] shortcut to define mutually exclusive components more ergonomically
  • Allow to define groups to make it possible to add components to be mutually exclusive with multiple others without having to list them all.
  • Make Query filters aware of mutually exclusive components so that entities with different mutually exclusive components don't cause conflicts.

Testing

bundle/tests.rs contains new tests for basic functionality, miri passes.

@Jondolf Jondolf added C-Feature A new feature, making something new possible A-ECS Entities, components, systems, and events M-Release-Note Work that should be called out in the blog due to impact D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes D-Unsafe Touches with unsafe code in some way S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Feb 5, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Feb 5, 2026

It looks like your PR has been selected for a highlight in the next release blog post, but you didn't provide a release note.

Please review the instructions for writing release notes, then expand or revise the content in the release notes directory to showcase your changes.

Copy link
Contributor

@Jondolf Jondolf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this a lot! This has been something that I'd really like for Avian, as we've been planning to split the RigidBody enum into mutually exclusive DynamicBody, KinematicBody, and StaticBody components. This is already possible to hack together via hooks, but it's not ideal, and the lack of a first-party "mutual exclusiveness" invariant has kept me wary of using the pattern.

I did an initial cursory review of the code, and it looks pretty solid already. I like the tests! I'm not too familiar with the archetype code though, so I'll defer checking its correctness to the ECS wizards :)

Some general initial thoughts:

  • I like starting minimal here, and a World API is enough for my purposes. More user-facing derive shorthands or Query integration would be nice, but can totally be left to future PRs IMO.
  • I like the default behavior of removing existing components that are incompatible with a new insertion, and panicking when adding two exclusive components simultaneously. I can see others maybe wanting different behavior in some cases, like not inserting an incompatible component instead of replacing an existing one, or emitting a warning instead of panicking, but I think the behavior in this PR is the right default. If other options are strongly desired, we can consider adding them in other PRs.
  • Some usage docs and examples could be nice! Though right now, this is not as user-facing as it's only a World API.

I'll try to do a more thorough review later

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-ECS Entities, components, systems, and events C-Feature A new feature, making something new possible D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes D-Unsafe Touches with unsafe code in some way M-Release-Note Work that should be called out in the blog due to impact S-Needs-Review Needs reviewer attention (from anyone!) to move forward

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants