Skip to content

Add SkipIfAny SystemParam - #25852

Open
BrainBacon wants to merge 3 commits into
bevyengine:mainfrom
greymattergames:feature/skip-if-any
Open

BrainBacon wants to merge 3 commits into
bevyengine:mainfrom
greymattergames:feature/skip-if-any

Conversation

@BrainBacon

@BrainBacon BrainBacon commented Sep 20, 2026

Copy link
Copy Markdown

Objective

Bevy has ergonomic system params for Single and Populated which skip system execution when the condition isn't met.

  • Query expects any amount of results
  • Single is expecting one result
  • Populated is expecting more than one result

This leaves space for a potential addition expecting zero results.

Relevant Discord discussion starting here

Solution

I added a new system param called SkipIfAny<F> that will skip the system if any results match the provided filter.

This does come with the caveat that there is no data returned by SkipIfAny, so in order to avoid any unused warnings, users should add an underscore before their argument names.

Alternatives Considered

Alternatively, a similar result can be achieved via a system run condition e.g. not(any_match_filter::<F>). I find that still has value in certain situations like when running the same system in multiple contexts, but I find SkipIfAny to be a nice way to ensure enforcement of the condition at the system level.

We could potentially add something like SkipIf<P: SystemParam> which would allow generalization for situations like SkipIf<Populated<(), With<MyComponent>> and would achieve the same result. Ultimately I decided to submit this change anyway because it is really straightforward and a bit more ergonomic. SkipIf is still a potential solution that could be added alongside this PR.

Testing

I've implemented a similar setup in my own game and refactored several systems that match the use case (although that implementation of SkipIfAny uses Query::query_unchecked instead of Query::get_param since Tick is not accessible to outside consumers).

I also refactored the Irradiance Volumes example since it was the best existing example that fit the use case and ran it to verify that it still functions as expected.

Future Work

Outside of a generalized solution, other ergonomic control flow helpers like this could be added such as one that skips unless more than one result is found or if only one is found. I haven't had need of these helpers in my own project, but if I find them valuable I can upstream them as well.


Showcase

In my own codebase

Before:

fn first_ethernet_connection_observer(
    _trigger: On<Start<PrimaryAction>>,
    mut commands: Commands,
    descriptors: DescriptorParam,
    target: Single<Entity, (With<CursorOverComponent>, With<EthernetNICComponent>)>,
    selection: Query<(), With<WireSelectionComponent>>,
) {
    if !selection.is_empty() {
        return;
    }

    if !descriptors.is_spawnable_selected(SpawnableVariant::Wire { variant: WireVariant::Ethernet }) {
        return;
    }

    commands.entity(*target).try_insert(UniqueComponent(WireSelectionComponent));
}

After:

fn first_ethernet_connection_observer(
    _trigger: On<Start<PrimaryAction>>,
    mut commands: Commands,
    descriptors: DescriptorParam,
    target: Single<Entity, (With<CursorOverComponent>, With<EthernetNICComponent>)>,
    _skip_selection: SkipIfAny<With<WireSelectionComponent>>,
) {
    if !descriptors.is_spawnable_selected(SpawnableVariant::Wire { variant: WireVariant::Ethernet }) {
        return;
    }

    commands.entity(*target).try_insert(UniqueComponent(WireSelectionComponent));
}

@github-actions

Copy link
Copy Markdown
Contributor

Welcome, new contributor!

Please make sure you've read our contributing guide, as well as our policy regarding AI usage, and we look forward to reviewing your pull request shortly ✨

@BrainBacon
BrainBacon force-pushed the feature/skip-if-any branch 2 times, most recently from 3e72cff to 27528d9 Compare September 20, 2026 01:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant