Expand description
§
Utility for tracking asset reloads and propagating changes to dependent ECS data.
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 scheduling with AssetTrackingPlugin and TrackAssetPlugin. |
| 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 automatically configure TrackAssetSystems in a schedule of your choice. Most users will want to handle changes in PostUpdate.
use bevy::prelude::*;
use bevy_track_asset::prelude::*;
fn main() {
App::new()
.add_plugins((
DefaultPlugins,
AssetTrackingPlugin::<PostUpdate>::default(),
));
}§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 a TrackAssetPlugin for your asset type and a system param that can extract the dependent items (e.g. Query<&mut ShaderUser>). This will add a set_changed_on_asset_reload_system to your app, which listens for asset events and marks dependent items as changed when their asset is reloaded.
app.add_plugins((
TrackAssetPlugin::<MyShader, Query<&mut ShaderUser>>::default(),
TrackAssetPlugin::<MyShader, TrackResMut<ShaderUser>>::default(),
));§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
Asset Plugin - Convenience plugin for tracking asset changes in the
TrackAssetSystems::Watchersystem set duringPostUpdate. - 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.