Use of .is_resource_changed in a closure
- Dominant language
- Rust
- Stars
- 48.2k
- Forks
- 4.8k
- Avg merge
- 3d 16h
- Merged PRs (30d)
- 171
Description
## Bevy version and features
I am using bevy 0.17 and rust 1.88
## What you did
So this isnt a bug per se but consider this. world.is_resource_changed uses its 'wrapping local system' to manage its state and therefore if you use it in a closure, you get crazy undefined results. Not really ideal.
```
list_container.element_factory(
(
Node {
..default()
},
),
move |world: &World | {
let Some(item_instance_list_comp) = world.get::(root_node.clone()) else { return false };
let item_instance_list_source = &item_instance_list_comp.item_instance_list_source;
item_instance_list_source.is_changed( world ) // this will not fire properly !!!
}) ;
```
```
impl ItemInstanceListSource {
pub fn is_changed(&self, world: &World) -> bool {
world.is_resource_changed::< T> ()
}
}
#[derive(Component)]
pub struct UiElementFactoryComponent {
pub needs_rebuild: Box bool + Send + Sync>,
pub children_output: Box Option<&Vec> + Send + Sync>,
pub spawn_children: Box, &T) + Send + Sync>,
}
pub trait UiElementFactoryExt {
fn element_factory(
&mut self,
bundle: impl Bundle,
needs_rebuild: impl Fn(&World) -> bool + Send + Sync + 'static,
children_output: impl Fn(&World) -> Option< &Vec > + Send + Sync + 'static,
spawn_children: impl Fn(&mut UiBuilder, &T) + Send + Sync + 'static,
) -> UiBuilder;
}
impl UiElementFactoryExt for UiBuilder<'_, UiRoot> {
fn element_factory(
&mut self,
bundle: impl Bundle,
needs_rebuild: impl Fn(&World) -> bool + Send + Sync + 'static,
children_output: impl Fn(&World) -> Option< &Vec > + Send + Sync + 'static ,
spawn_children: impl Fn(&mut UiBuilder, &T) + Send + Sync + 'static,
) -> UiBuilder {
let new_builder = self.spawn(UiElementFactoryComponent {
needs_rebuild: Box::new(needs_rebuild),
children_output: Box::new(children_output),
spawn_children: Box::new(spawn_children),
});
new_builder
}
}
fn check_for_rebuild(
element_factory_query: Query<(
Entity,
&UiElementFactoryComponent,
Option<&UiElementFactoryInitialized>,
)>,
world: &World,
mut commands: Commands,
) {
if (factory_comp.needs_rebuild)(world) {
// THIS WILL NOT RUN PROPERLY !!!! < -----------
}
}
```
## What went wrong
So I am pretty sure I know why the logic doesnt run properly when the resource is_changed. I read the docs for is_resource_changed and it does say that it tells you if the resource has changed SINCE the last time the condition was checked. I imagine this is storing some kind of state in the 'system' level and what seems to be happening with the above code is the state is being stored IN THE CLOSURE meaning it only runs once -- it is essentially frozen in time. Running the closure does not properly reset that state.
## Additional information
I would like to be able to use some kind of is_resource_changed maybe an alternate kind that will work in a closure .! For my game, i am no longer trying to access is_resource_changed through a closure instead i have opted to use a special caching middleman resource to make this work. But it would be nice if i could do this in a cleaner way.
For example. maybe we could get a function like is_resource_changed_per_frame that uses more of a 'global' state versus a 'local system' state so that could be used in a closure.. and it gets reset in like Last or First or something. i dont know x.x
Contributor guide
Research direction
Start with the documented World::is_resource_changed entry point and its wrapping local system behavior, then trace how check_for_rebuild invokes the stored Fn(&World) closure. The issue is complete only after maintainers decide on a supported closure-safe change-detection approach and define its behavior for repeated calls.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- game-dev
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100