Expand description
§
Propagate asset changes to dependent components and resources in Bevy.
When working with assets in Bevy, it’s common to cache derived data that depends on an asset. For example, a resource that holds the output of a compute shader may need to be re-computed whenever an asset it depends on changes.
This crate provides a composable system, set_changed_on_asset_reload_system, along with the
TrackAsset, AssetDependent, and TriggerChangeDetection traits, to automate this
pattern: whenever a tracked asset is modified or finishes loading, all dependent ECS items
(components, resources, etc.) are automatically marked as changed, so that downstream systems
can react using Bevy’s standard change detection.
§Feature flags
| Default | Feature | Description |
|---|---|---|
| Yes | bevy_app | Enables automatic configuration of TrackAssetSystems via AssetTrackingPlugin. |
| Yes | bevy_log | Logs asset change events and warnings about missing plugins in debug builds. |
§Getting Started
Add an AssetTrackingPlugin to your Bevy app to configure TrackAssetSystems in the desired schedule. Most users will want to handle changes in PostUpdate and can just
use AssetTrackingPlugin::default().
use bevy::prelude::*;
use bevy_track_asset::prelude::*;
fn main() {
App::new()
.add_plugins((
DefaultPlugins,
AssetTrackingPlugin::default(), // AssetTrackingPlugin::<PostUpdate>::new(),
));
}§Declaring dependencies
Implement AssetDependent for your component, resource, or custom system param item,
returning the AssetId of the asset it depends on:
use bevy::prelude::*;
use bevy_track_asset::AssetDependent;
#[derive(Asset, TypePath)]
struct MyShader { /* ... */ }
#[derive(Component, Resource, Default)]
struct ComputedValue { /* ... */ }
#[derive(Component, Resource)]
#[require(ComputedValue)]
struct ShaderUser {
shader: Handle<MyShader>,
// ...
}
impl AssetDependent<MyShader> for ShaderUser {
fn asset_id(&self) -> AssetId<MyShader> {
self.shader.id()
}
}§Tracking the asset
Add set_changed_on_asset_reload_system to your app, specifying the asset type and a
system param that provides access to the items you want to track (e.g. Query<&mut ShaderUser>).
The system is automatically added to the Watcher system set, but may be further configured.
app.add_systems(PostUpdate,
(
set_changed_on_asset_reload_system::<MyShader, Query<Mut<ShaderUser>>>(),
set_changed_on_asset_reload_system::<MyShader, TrackResMut<ShaderUser>>(),
)
);§Reacting to changes
Systems can handle changes with standard bevy change detection patterns.
use bevy::prelude::*;
use bevy_track_asset::prelude::*;
fn main() -> AppExit {
App::new()
// ...other plugins
.add_systems(PostUpdate, handle_changed_system.in_set(TrackAssetSystems::Reload))
.run()
}
fn handle_changed_system(
mut query: Query<(&ShaderUser, &mut ComputedValue), Changed<ShaderUser>>,
assets: Res<Assets<MyShader>>
) {
for (user, mut value) in &mut query {
// Re-derive cached data from the reloaded asset...
let shader = assets.get(&user.shader).unwrap();
*value = shader.compute(user);
}
}§Custom SystemParam
You can also use custom system params to track more complex dependencies, such as multiple
components or resources that depend on the same asset type. Just ensure that the Item type of
your system param implements IntoIterator over elements that are both AssetDependent and
TriggerChangeDetection, and the change-tracking system will handle the rest.
The following example demonstrates a custom system parameter (UsingImage), that tracks
dependencies of Image (UseImage – both a component and a resource), and marks them as changed
when the image behind their Handle<Image> is loaded or reloaded.
use bevy::{
ecs::{
query::QueryIter,
system::{SystemParam, lifetimeless::Write},
},
prelude::*,
};
use bevy_track_asset::prelude::*;
/// Depends on an `Image` asset, and will be marked as changed when the asset reloads.
#[derive(Component, Resource)]
struct UseImage(Handle<Image>);
/// Iterator item returned by the custom system param.
enum ImageDependency<'w> {
QueryUsesImage(Mut<'w, UseImage>),
GlobalUseImage(ResMut<'w, UseImage>),
}
impl AssetDependent<Image> for ImageDependency<'_> {
#[inline]
fn asset_id(&self) -> AssetId<Image> {
match self {
ImageDependency::QueryUsesImage(component) => component.0.id(),
ImageDependency::GlobalUseImage(resource) => resource.0.id(),
}
}
}
// Required to mark items changed when the asset reloads.
impl TriggerChangeDetection for ImageDependency<'_> {
#[inline]
fn trigger_change(&mut self) {
match self {
ImageDependency::QueryUsesImage(component) => component.set_changed(),
ImageDependency::GlobalUseImage(resource) => resource.set_changed(),
}
}
}
/// Example of a custom system param that
/// marks resources and components as changed
/// when their associated asset is modified.
#[derive(SystemParam)]
struct UsingImage<'w, 's> {
query_uses_image: Query<'w, 's, Write<UseImage>>,
global_use_image: ResMut<'w, UseImage>,
}
impl<'w, 's> IntoIterator for UsingImage<'w, 's> {
type Item = ImageDependency<'w>;
type IntoIter = std::iter::Chain<
std::iter::Map<
QueryIter<'w, 's, Write<UseImage>, ()>,
fn(Mut<'w, UseImage>) -> ImageDependency<'w>,
>,
std::iter::Once<ImageDependency<'w>>,
>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.query_uses_image
.into_iter()
.map(
ImageDependency::QueryUsesImage
as fn(bevy::prelude::Mut<'w, UseImage>) -> ImageDependency<'w>,
)
.chain(std::iter::once(ImageDependency::GlobalUseImage(
self.global_use_image,
)))
}
}Check out the full example in examples/custom_param.rs for a working
demonstration of this pattern.
Modules§
Structs§
- Asset
Tracking Plugin - Plugin that configures
TrackAssetSystemsin a schedule of your choice. - Track
ResMut - A wrapper for
ResMut<T>that implementsTrackAsset, for tracking assets in resources.
Enums§
- Track
Asset Systems - System sets for asset tracking systems. With the
bevy_appfeature enabled, these are automatically configured byAssetTrackingPlugin. Withoutbevy_app, you need to configure these yourself.
Traits§
- Asset
Dependent - Declares that an ECS item depends on a specific asset, and can provide its
AssetId. - Track
Asset - A marker trait for
SystemParams whose data depends on an asset and can trigger change detection when the asset is changed. - Trigger
Change Detection - Allows an ECS item to be flagged as needing to be re-derived from its associated asset.
Functions§
- extract_
asset_ id - Extracts the
AssetIdfrom anAssetEventif it represents a meaningful change. - set_
changed_ on_ asset_ reload_ system - A system that marks ECS items as changed when the asset they depend on is modified or reloaded.