How to Define Custom Assets
This guide explains how to define a new asset type to be used in an Amethyst application. If you are defining a new asset type that may be useful to others, please send us a PR!
-
Define the type and handle for your asset.
extern crate amethyst; extern crate serde_derive; use amethyst::{ assets::Handle, ecs::VecStorage, }; /// Custom asset representing an energy blast. #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct EnergyBlast { /// How much HP to subtract. pub hp_damage: u32, /// How much MP to subtract. pub mp_damage: u32, } /// A handle to a `EnergyBlast` asset. pub type EnergyBlastHandle = Handle<EnergyBlast>;
-
Define the type that represents the serializable form of the asset.
The serializable type can be one of:
-
The asset type itself, in which case you simply derive
Serialize
andDeserialize
on the type:#[derive(Serialize, Deserialize, ..)] pub struct EnergyBlast { .. }
-
An enum with different variants – each for a different data layout:
extern crate serde_derive; use serde_derive::{Deserialize, Serialize}; /// Separate serializable type to support different versions /// of energy blast configuration. #[derive(Clone, Debug, Deserialize, Serialize)] pub enum EnergyBlastData { /// Early version only could damage HP. Version1 { hp_damage: u32 }, /// Add support for subtracting MP. Version2 { hp_damage: u32, mp_damage: u32 }, }
-
-
Implement the
Asset
trait on the asset type.extern crate amethyst; extern crate serde_derive; use amethyst::{ assets::{Asset, Handle}, ecs::VecStorage, }; use serde_derive::{Deserialize, Serialize}; /// Custom asset representing an energy blast. #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct EnergyBlast { /// How much HP to subtract. pub hp_damage: u32, /// How much MP to subtract. pub mp_damage: u32, } impl Asset for EnergyBlast { const NAME: &'static str = "my_crate::EnergyBlast"; // use `Self` if the type is directly serialized. type Data = EnergyBlastData; type HandleStorage = VecStorage<EnergyBlastHandle>; } /// A handle to a `EnergyBlast` asset. pub type EnergyBlastHandle = Handle<EnergyBlast>; /// Separate serializable type to support different versions /// of energy blast configuration. #[derive(Clone, Debug, Deserialize, Serialize)] pub enum EnergyBlastData { /// Early version only could damage HP. Version1 { hp_damage: u32 }, /// Add support for subtracting MP. Version2 { hp_damage: u32, mp_damage: u32 }, }
-
Implement the
ProcessableAsset
trait, providing the conversion function forA::Data
into aProcessingState<A>
result.The
Processor<A>
system uses this trait to convert the deserialized asset data into the asset.extern crate amethyst; extern crate serde_derive; use amethyst::{ error::Error, assets::{Asset, Handle, ProcessingState, ProcessableAsset}, ecs::VecStorage, }; use serde_derive::{Deserialize, Serialize}; /// Custom asset representing an energy blast. #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct EnergyBlast { /// How much HP to subtract. pub hp_damage: u32, /// How much MP to subtract. pub mp_damage: u32, } /// A handle to a `EnergyBlast` asset. pub type EnergyBlastHandle = Handle<EnergyBlast>; impl Asset for EnergyBlast { const NAME: &'static str = "my_crate::EnergyBlast"; // use `Self` if the type is directly serialized. type Data = EnergyBlastData; type HandleStorage = VecStorage<EnergyBlastHandle>; } /// Separate serializable type to support different versions /// of energy blast configuration. #[derive(Clone, Debug, Deserialize, Serialize)] pub enum EnergyBlastData { /// Early version only could damage HP. Version1 { hp_damage: u32 }, /// Add support for subtracting MP. Version2 { hp_damage: u32, mp_damage: u32 }, } impl ProcessableAsset for EnergyBlast { fn process(energy_blast_data: Self::Data) -> Result<ProcessingState<Self>, Error> { match energy_blast_data { EnergyBlastData::Version1 { hp_damage } => { Ok(ProcessingState::Loaded(Self { hp_damage, ..Default::default() })) } EnergyBlastData::Version2 { hp_damage, mp_damage } => { Ok(ProcessingState::Loaded(Self { hp_damage, mp_damage, })) } } } }
If your asset is stored using one of the existing supported formats such as RON or JSON, it can now be used:
extern crate amethyst; extern crate serde_derive; use amethyst::{ error::Error, assets::{AssetStorage, Loader, ProcessableAsset, ProcessingState, ProgressCounter, RonFormat}, ecs::{World, WorldExt}, prelude::*, utils::application_root_dir, }; use serde_derive::{Deserialize, Serialize}; use amethyst::{ assets::{Asset, Handle}, ecs::VecStorage, }; /// Custom asset representing an energy blast. #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct EnergyBlast { /// How much HP to subtract. pub hp_damage: u32, /// How much MP to subtract. pub mp_damage: u32, } /// A handle to a `EnergyBlast` asset. pub type EnergyBlastHandle = Handle<EnergyBlast>; /// Separate serializable type to support different versions /// of energy blast configuration. #[derive(Clone, Debug, Deserialize, Serialize)] pub enum EnergyBlastData { /// Early version only could damage HP. Version1 { hp_damage: u32 }, /// Add support for subtracting MP. Version2 { hp_damage: u32, mp_damage: u32 }, } impl Asset for EnergyBlast { const NAME: &'static str = "my_crate::EnergyBlast"; // use `Self` if the type is directly serialized. type Data = EnergyBlastData; type HandleStorage = VecStorage<EnergyBlastHandle>; } impl ProcessableAsset for EnergyBlast { fn process(energy_blast_data: Self::Data) -> Result<ProcessingState<Self>, Error> { match energy_blast_data { EnergyBlastData::Version1 { hp_damage } => { Ok(ProcessingState::Loaded(Self { hp_damage, ..Default::default() })) } EnergyBlastData::Version2 { hp_damage, mp_damage } => { Ok(ProcessingState::Loaded(Self { hp_damage, mp_damage, })) } } } } pub struct LoadingState { /// Tracks loaded assets. progress_counter: ProgressCounter, /// Handle to the energy blast. energy_blast_handle: Option<EnergyBlastHandle>, } impl SimpleState for LoadingState { fn on_start(&mut self, data: StateData<'_, GameData<'_, '_>>) { let loader = &data.world.read_resource::<Loader>(); let energy_blast_handle = loader.load( "energy_blast.ron", RonFormat, &mut self.progress_counter, &data.world.read_resource::<AssetStorage<EnergyBlast>>(), ); self.energy_blast_handle = Some(energy_blast_handle); } } fn main() -> amethyst::Result<()> { let app_root = application_root_dir()?; let assets_dir = app_root.join("assets"); let game_data = GameDataBuilder::default(); let mut game = Application::new( assets_dir, LoadingState { progress_counter: ProgressCounter::new(), energy_blast_handle: None, }, game_data, )?; game.run(); Ok(()) }
If the asset data is stored in a format that is not supported by Amethyst, a custom format can be implemented and provided to the
Loader
to load the asset data.