#Running one system with two different, disjoint, change detection filter sets

9 messages · Page 1 of 1 (latest)

vocal bramble
#

I've often found myself wanting to run the same system code on the same query data, but in more than one possible combination of change detection filters. To avoid an impending XY problem, this usually happens when:

  • I have one bit of state (such as a UI 'view') that is tracking another bit of state (the 'model', I guess);
  • One of the change detection filter sets is firing the system once when the 'view' is created, to set the initial state;
  • The other one is firing the system every time the 'model' is updated, to update the state;
  • The queries being filtered are disjoint (meaning I can't just use Or).

A random example from a bit of tile editor UI code:

pub(super) fn backpropagate_pixel_states<C: QueryFilter, P: QueryFilter>(
    buffer: Single<&MainBuffer, C>,    // model: some buffer containing pixel data
    mut pixels: Query<&mut Pixel, P>,  // view: some UI elements displaying individual pixel values
) {
    // ...
}

// in the plugin:
app.add_systems(Update, (
    systems::backpropagate_pixel_states::<(), Added<Pixel>>,
    systems::backpropagate_pixel_states::<Changed<MainBuffer>, ()>,
))

This works, but feels clunky, and makes me wonder whether there is either a more obvious way to handle these kinds of 'use a system in multiple different ways' (maybe not using change detection filters), or whether it's entirely an anti-pattern and I should be breaking this up in a different way (using observers or events, etc).

Any pointers would be very helpful!

Thanks!

#

Another even weirder situation I've got myself into is that I want to be able to recalculate a UI position that's dependent on a world-space position, but the UI can re-scale, meaning I have a system that needs to listen either for a component change through a query filter or a resource change through a run condition...?!

pub fn update_cursor_position<F: QueryFilter>(
    camera: Single<(&Camera, &GlobalTransform)>,
    query: Query<UpdateCursorPositionQuery, (With<Cursor>, F)>,
) { /* ... */ }

app.add_systems(Update, (
    systems::update_cursor_position::<()>.run_if(resource_changed::<UiScale>),
    systems::update_cursor_position::<Changed<board::Position>>
));
rare pivot
#

can access the changeticks directly

vocal bramble
#

ah, is that the Ref smart pointer?

rare pivot
#

yep

#

or Mut

vocal bramble
#

I had seen that, but never thought to use it! 😭 I think I convinced myself for some reason that it'd cause perf problems or something. (Despite literally deciding to instead register two monomorphised copies of the same system into each schedule...)

rare pivot
#

Changed isn't archetypal (besides the implicit With), so it has to check entities when you iter or get

#

also Res is a wrapper over Ref that impls SystemParam instead of QueryData