How to Define Prefabs: Adapter
This guide explains how to define a [PrefabData
] for a [Component
] using an intermediate type called an adapter. This pattern is used when there are multiple ways to serialize / construct the [Component
]:
extern crate amethyst;
extern crate serde;
use amethyst::ecs::{storage::DenseVecStorage, Component};
use serde::{Deserialize, Serialize};
#[derive(Component, Debug, Deserialize, Serialize /* .. */)]
pub struct Position(pub f32, pub f32, pub f32);
impl From<(i32, i32, i32)> for Position {
fn from((x, y, z): (i32, i32, i32)) -> Position {
Position(x as f32, y as f32, z as f32)
}
}
impl From<(f32, f32, f32)> for Position {
fn from((x, y, z): (f32, f32, f32)) -> Position {
Position(x, y, z)
}
}
If you are attempting to adapt a more complex type, please choose the appropriate guide from the [available guides][bk_prefab_prelude].
Steps
-
Ensure your crate has the following dependencies:
[dependencies] amethyst = ".." # Minimum version 0.10 serde = { version = "1.0", features = ["derive"] }
-
Define the adapter prefab data type.
Create a (de)serializable enum type with a variant for each representation. The following is an example of an adapter type for the [
Position
] component, which allows eitheri32
orf32
values to be specified in the prefab:extern crate amethyst; extern crate serde; use amethyst::{ assets::{PrefabData, ProgressCounter}, ecs::{Entity, WriteStorage}, Error, }; use serde::{Deserialize, Serialize}; #[derive(Clone, Copy, Deserialize, PartialEq, Serialize)] #[serde(deny_unknown_fields)] pub enum PositionPrefab { Pos3f { x: f32, y: f32, z: f32 }, Pos3i { x: i32, y: i32, z: i32 }, }
The [
#[serde(deny_unknown_fields)]
] ensures that deserialization produces an error if it encounters an unknown field. This will help expose mistakes in the prefab file, such as when there is a typo.Note: You may already have a type that captures the multiple representations. For example, for the [
Camera
] component, the [Projection
] enum captures the different representations:extern crate amethyst; extern crate serde; use amethyst::core::math::{Orthographic3, Perspective3}; use serde::{Deserialize, Serialize}; #[derive(Clone, Deserialize, PartialEq, Serialize)] pub enum Projection { Orthographic(Orthographic3<f32>), Perspective(Perspective3<f32>), }
-
Implement the [
PrefabData
] trait for the adapter type.extern crate amethyst; extern crate serde; use amethyst::{ assets::{PrefabData, ProgressCounter}, ecs::{storage::DenseVecStorage, Component, Entity, WriteStorage}, Error, }; use serde::{Deserialize, Serialize}; #[derive(Component, Debug, Deserialize, Serialize /* .. */)] pub struct Position(pub f32, pub f32, pub f32); impl From<(i32, i32, i32)> for Position { fn from((x, y, z): (i32, i32, i32)) -> Position { Position(x as f32, y as f32, z as f32) } } impl From<(f32, f32, f32)> for Position { fn from((x, y, z): (f32, f32, f32)) -> Position { Position(x, y, z) } } #[derive(Clone, Copy, Deserialize, PartialEq, Serialize)] #[serde(deny_unknown_fields)] pub enum PositionPrefab { Pos3f { x: f32, y: f32, z: f32 }, Pos3i { x: i32, y: i32, z: i32 }, } impl<'a> PrefabData<'a> for PositionPrefab { // To attach the `Position` to the constructed entity, // we write to the `Position` component storage. type SystemData = WriteStorage<'a, Position>; // This associated type is not used in this pattern, // so the empty tuple is specified. type Result = (); fn add_to_entity( &self, entity: Entity, positions: &mut Self::SystemData, _entities: &[Entity], _children: &[Entity], ) -> Result<(), Error> { let position = match *self { PositionPrefab::Pos3f { x, y, z } => (x, y, z).into(), PositionPrefab::Pos3i { x, y, z } => (x, y, z).into(), }; positions.insert(entity, position).map(|_| ())?; Ok(()) } }
-
Now the adapter type can be used in a prefab to attach the component to the entity.
#![enable(implicit_some)] Prefab( entities: [ PrefabEntity( data: Pos3f(x: 1.0, y: 2.0, z: 3.0), ), PrefabEntity( data: Pos3i(x: 4, y: 5, z: 6), ), ], )
To see this in a complete example, run the [prefab_adapter
example][repo_prefab_adapter] from the Amethyst repository:
cargo run --example prefab_adapter
[#[serde(default)]
]: https://serde.rs/container-attrs.html#default
[#[serde(deny_unknown_fields)]
]: https://serde.rs/container-attrs.html#deny_unknown_fields
[Camera
]: https://docs.amethyst.rs/stable/amethyst_renderer/struct.Camera.html
[Component
]: https://docs.amethyst.rs/stable/specs/trait.Component.html
[Prefab
]: https://docs.amethyst.rs/stable/amethyst_assets/struct.Prefab.html
[PrefabData
]: https://docs.amethyst.rs/stable/amethyst_assets/trait.PrefabData.html#impl-PrefabData%3C%27a%3E
[Projection
]: https://docs.amethyst.rs/stable/amethyst_renderer/enum.Projection.html
[bk_prefab_prelude]: how_to_define_prefabs_prelude.html
[repo_prefab_adapter]: https://github.com/amethyst/amethyst/tree/master/examples/prefab_adapter