Expand description
This crate loads and resolves assets that reference other assets by name.
It builds on serde and integrates into bevy’s asset ecosystem.
Hand-written asset types use Handles, which aren’t serializable. This
crate generates a serializable “Def” counterpart for each type, using plain
strings in place of Handles, along with a FromDef impl that
converts a Def into its runtime type — resolving each string into a
Handle by using LoadContext::load() along the way.
§Basic usage
Let’s say you have an animation asset water_animation.ron, which references its spritesheet
by name:
(
frames: [1, 2, 3,],
frame_duration: (
secs: 0,
nanos: 128000000,
),
spritesheet: "water",
)The corresponding struct would look something like this:
#[derive(FromDef, Asset, TypePath)]
struct AnimationAsset {
frames: Vec<usize>,
frame_duration: Duration,
spritesheet: Handle<Spritesheet>,
}The FromDef derive macro generates a (de)serializable version of the struct as well as an
implementation of the FromDef trait, which converts it into your struct. The generated
struct looks something like this:
#[derive(Serialize, Deserialize)]
struct AnimationDef {
frames: Vec<usize>,
frame_duration: Duration,
spritesheet: String,
}Assets, that implement FromDef can be loaded with the RonAssetLoader, which calls
FromDef::from_def() to convert the raw deserialized structure into the runtime
structure. You can register the asset and the RonAssetLoader manually or use the AppExt extension trait:
app.init_ron_asset::<AnimationAsset>().init_ron_asset::<Spritesheet>();To resolve the string names into handles some metadata needs to be provided. Let’s take the Spritesheet asset as an example:
#[derive(FromDef, Asset, TypePath)]
#[asset_spec(base_path = "spritesheets", extension = "ron")]
struct Spritesheet { /* fields omitted */ }With the asset_spec provided the spritesheet handles inside the AnimationAsset can now be
resolved, e.g. the “water” spritesheet gets resolved into “spritesheets/water.ron”.
§Resolving foreign types with elf
Asset types you don’t own cannot be annotated with attributes. Let’s take a closer look at the Spritesheet asset:
#[derive(FromDef, Asset, TypePath)]
#[asset_spec(base_path = "spritesheets", extension = "ron")]
struct Spritesheet {
#[elf(with_spec(base_path = "spritesheets/images", extension = "png"))]
image: Handle<Image>,
#[elf(with_spec(base_path = "spritesheets/layouts", extension = "ron"))]
layout: Handle<TextureAtlasLayout>,
}Since Image and TextureAtlasLayout are not defined by you they cannot be resolved
the same way, because they don’t have an asset_spec.
For these you can use the elf field attribute to tell bevy_elf how to resolve them as shown above.
§Implicit fields
It is also possible to make asset references implicit by their name. Imagine your assets are organized in the file system like this:
assets/
├── animations/
│ ├── water.ron
│ └── grass.ron
└── spritesheets/
├── water.ron
├── grass.ron
├── images/
│ ├── water.png
│ └── grass.png
└── layouts/
├── water.ron
└── grass.ronExplicitly mentioning e.g. “water” everywhere is cumbersome. Make it implicit instead. The
implicit flag goes along well with sub_path:
#[derive(FromDef, Asset, TypePath)]
#[asset_spec(base_path = "spritesheets", extension = "ron")]
struct Spritesheet {
#[elf(implicit, with_spec(sub_path = "images", extension = "png"))]
image: Handle<Image>,
#[elf(implicit, with_spec(sub_path = "layouts", extension = "ron"))]
layout: Handle<TextureAtlasLayout>,
}That way the image and layout fields are omitted in the generated def type and don’t show up
in the ron file at all. They are resolved with the same string name as their parent.
§Omitting empty def files
With the fields being implicit the spritesheet ron files are now empty. Having to put an empty
file there for it all to work isn’t nice at all! To omit the whole file tell bevy_elf to omit
the def type entirely and use () instead. Just tell the referencing AnimationAsset to not
load any file but put a default value into FromDef::from_def() instead:
#[derive(FromDef, Asset, TypePath)]
struct AnimationAsset {
frames: Vec<usize>,
frame_duration: Duration,
#[elf(from_default)]
spritesheet: Handle<Spritesheet>,
}
#[derive(FromDef)]
#[elf(def_type(()))]
struct Spritesheet {
#[elf(implicit, with_spec(base_path = "images", extension = "png"))]
image: Handle<Image>,
#[elf(implicit, with_spec(base_path = "layouts", extension = "ron"))]
layout: Handle<TextureAtlasLayout>,
}That way your directory structure, as well as the ron files themselves become leaner and
the spritesheet doesn’t appear in the file system at all anymore.
But the image and its layout are still neatly stored in their own Spritesheet struct.
assets/
├── animations/
│ ├── water.ron
│ └── grass.ron
├── images/
│ ├── water.png
│ └── grass.png
└── layouts/
├── water.ron
└── grass.ronNote that since the spritesheets directory doesn’t exist anymore, images/ and layouts/
move up to become top-level asset folders, so we changed from sub_path back to base_path.
Also note, that Spritesheet is no Asset anymore, since it doesn’t get loaded from a file,
so the registration via app.init_ron_asset::<Spritesheet>(); disappears as well.
For more attributes and options see FromDef.
Structs§
- Asset
Ref - A
Handlewith the assets string id preserved. - Dynamic
Path Resolver - Path
Resolver - An asset resolver, that assumes the given string id is the complete asset path and returns it
as an
AssetPathunchanged. - Resolver
Spec - An adapter type, that implements
AssetResolverby using anAssetPathSpec(S). - RonAsset
Loader - Loads assets, which implement
FromDeffrom ron files passing the deserializedFromDef::Defvalue into the assetsFromDef::from_defmethod. - Spec
Resolver - Static
Resolver Adapter - An adapter type, that implements
AssetResolverby delegating to aStaticAssetResolver(S)
Enums§
Traits§
- AppExt
- Extension trait for
Appto register a ron asset with aFromDefimplementation and a correspondingRonAssetLoaderby callingapp.init_ron_asset::<MyRonAsset>(). - Asset
Path Spec - Asset
Path Spec Provider - An
AssetResolverusing abase_pathand an optional fileextension. - Asset
Resolver - Resolves the
AssetPathfor a given string id. - FromDef
- Represents a type, that can be constructed from a deserializable def type.
An asset type implementing
FromDefcan be loaded by theRonAssetLoader. It deserializes the asset bytes into theFromDef::Deftype and then turns it into the runtime asset type which implementsFromDefby passing it toFromDef::from_def(). This trait can be implemented manually or by using theFromDefderive macro. To enable loading ron assets implementingFromDefinit the asset and loader viaAppExt::init_ron_asset(). - From
DefWith Resolver - Like
FromDef, but uses an explicitly passedAssetResolverto resolve itsAssetPath. - HasResolver
- HasSpec
Provider - Static
Asset Resolver - Resolves the
AssetPathfor a given string asset id from a static context, meaning to self object is nessecary.
Functions§
- extract_
id_ from - Extracts the string id of the asset from its
AssetPath.
Attribute Macros§
- asset_
spec - Generates an implementation of
AssetPathSpecandHasResolverfor the annotated struct or enum. This enables this asset to be resolved from a file name prefix when used as a field of a type implementingFromDef.
Derive Macros§
- FromDef
- Implements the trait
FromDeffor the annotated struct or enum, by converting all contained fields via theirFromDef::from_def()implementation. So all fields have to implementFromDef. All primitive types, container types likeOption,VecandHashMap, as well asHandleandAssetRefimplementFromDef.