//! This example covers some more details about the derive macro. //! //! > The example itself doesn't actually do anything interesting, //! > it's just meant to give some more details on the derive macro. //! //! For schematics to be useful, they need to be serializable. //! Unfortunately, there are times when we want our type to contain data that can't //! effectively be serialized (asset handles, entities, etc.). //! //! This is where the [`Schematic::Input`] comes into play. //! The [`Schematic`] trait takes an `Input` type that we can use as an //! intermediate value from which we can deserialize and base our schematics on. //! By default, this is determined to just be `Self`. //! //! However, when we apply a schematic field attribute, //! the derive macro will then generate a new input type to accommodate //! those fields that require that intermediate step. //! //! For more details, check out the documentation on the derive macro. use bevy::prelude::*; use bevy::reflect::TypePath; use std::marker::PhantomData; use bevy_proto::prelude::*; use bevy_proto_backend::schematics::FromSchematicInput; fn main() { println!("This example doesn't do anything..."); } /// This struct will generate its own input type since it has at least one field /// marked with a `#[schematic]` attribute. /// /// To see what this generated input type looks like, scroll to the bottom of this file. #[derive(Component, Schematic, Reflect)] #[reflect(Schematic)] struct Foo { /// Assets can be loaded by marking any `Handle` field like so: #[schematic(asset)] lazy_asset: Handle, /// By default, assets are lazy loaded— they don't get loaded until /// the schematic is applied to an entity. /// To preload the asset as a dependency of the prototype, /// we can give it the `preload` argument: #[schematic(asset(preload))] preloaded_asset: Handle, /// Entities can also be handled succinctly using an attribute. /// To reference any entity within the prototype's hierarchy /// (the `EntityTree`), we can use the following attribute: #[schematic(entity)] entity: Entity, /// The above will panic if the entity could not be found within the tree. /// If this is expected, we can also use this attribute on an `Option`: #[schematic(entity)] optional_entity: Option, /// We can also easily convert to any type `U` from type `T` /// where `T` implements `From` by using the `from` attribute: #[schematic(from=[f32;3])] simple_from: Color, /// For more advanced conversions, we can use [`FromSchematicInput`]. /// This provides a lot more context since it's actually called during /// schematic application. /// It also uses the `from` attribute: #[schematic(from=String)] complex_from: EntityGroup, /// As a side note, all reflection attributes get passed to the generated /// input type. #[reflect(ignore)] _phantom: PhantomData, } #[derive(Reflect)] struct EntityGroup(Vec); // This implementation allows us to get a group of entities from the world // that all share the name provided by `String`. impl FromSchematicInput for EntityGroup { fn from_input(input: String, context: &mut SchematicContext) -> Self { let world = context.world_mut(); let mut query = world.query::<(Entity, &Name)>(); let group = query .iter(world) .filter(|(_, name)| name.as_str() == input) .map(|(entity, _)| entity) .collect(); Self(group) } } /// We can also use the `from` attribute on our entire container type /// in order to designate an existing type as the input. /// /// Keep in mind that because this is defining a custom input type already, /// we are not able to use the schematic attributes on our fields— /// that would require that we use the custom input type _and_ generate a new one! #[derive(Component, Schematic, Reflect)] #[reflect(Schematic)] #[schematic(from = String)] struct Bar(String); impl From for Bar { fn from(value: T) -> Self { Self(value.to_string()) } } // Below is the generated input type for `Foo`. // // As you can see, it's contained within an anonymous const block so that we don't // leak this type into the current scope. // // If it's desirable to have access to the generated type, // you can use `vis` argument in the `#[schematic(input)]` container attribute: // `#[schematic( input( vis = pub(crate) ) )]`. // If we do this, we can even give it a custom name like: // `#[schematic( input( name = MyCustomInput ) )]`. // // ----------------------------------------------------------------------- // const _: () = { // #[derive(::bevy::prelude::Reflect)] // pub struct FooInput { // lazy_asset: ::bevy_proto_backend::proto::ProtoAsset, // preloaded_asset: ::bevy_proto_backend::proto::ProtoAsset, // entity: ::bevy_proto_backend::tree::EntityAccess, // optional_entity: ::bevy_proto_backend::tree::EntityAccess, // simple_from: [f32; 3], // complex_from: String, // #[reflect(ignore)] // _phantom: PhantomData, // #[reflect(ignore)] // __phantom_ty__: ::core::marker::PhantomData (T)>, // } // impl ::bevy_proto_backend::schematics::FromSchematicInput> // for Foo // { // fn from_input( // __input__: FooInput, // __context__: &mut ::bevy_proto_backend::schematics::SchematicContext, // ) -> Self { // Self { // lazy_asset: match __input__.lazy_asset { // ::bevy_proto_backend::proto::ProtoAsset::AssetPath(ref path) => { __context__.world().resource::<::bevy::asset::AssetServer>().load(path) } // ::bevy_proto_backend::proto::ProtoAsset::HandleId(handle_id) => { __context__.world().resource::<::bevy::asset::AssetServer>().get_handle(handle_id) } // }, // preloaded_asset: match __input__.preloaded_asset { // ::bevy_proto_backend::proto::ProtoAsset::AssetPath(ref path) => { __context__.world().resource::<::bevy::asset::AssetServer>().load(path) } // ::bevy_proto_backend::proto::ProtoAsset::HandleId(handle_id) => { __context__.world().resource::<::bevy::asset::AssetServer>().get_handle(handle_id) } // }, // entity: >::from_input(__input__.entity, __context__), // optional_entity: as ::bevy_proto_backend::schematics::FromSchematicInput<::bevy_proto_backend::tree::EntityAccess>>::from_input(__input__.optional_entity, __context__), // simple_from: >::from_input(__input__.simple_from, __context__), // complex_from: >::from_input(__input__.complex_from, __context__), // _phantom: __input__._phantom, // } // } // } // impl ::bevy_proto_backend::schematics::Schematic for Foo { // type Input = FooInput; // fn apply( // __input__: &Self::Input, // __context__: &mut ::bevy_proto_backend::schematics::SchematicContext, // ) { // let __input__ = ::from_reflect( // &*::bevy::reflect::Reflect::clone_value(__input__), // ) // .unwrap_or_else(|| { // panic!( // "{} should have a functioning `FromReflect` impl", // std::any::type_name::() // ) // }); // let __input__ = >::from_input(__input__, __context__); // __context__ // .entity_mut() // .unwrap_or_else(|| { // panic!( // "schematic `{}` expected entity", // std::any::type_name::() // ) // }) // .insert(__input__); // } // fn remove( // __input__: &Self::Input, // __context__: &mut ::bevy_proto_backend::schematics::SchematicContext, // ) { // __context__ // .entity_mut() // .unwrap_or_else(|| { // panic!( // "schematic `{}` expected entity", // std::any::type_name::() // ) // }) // .remove::(); // } // fn preload_dependencies( // __input__: &mut Self::Input, // __dependencies__: &mut ::bevy_proto_backend::deps::DependenciesBuilder, // ) { // match __input__.preloaded_asset { // ::bevy_proto_backend::proto::ProtoAsset::AssetPath(ref path) => { // let _: Handle = __dependencies__.add_dependency(path.to_owned()); // } // ::bevy_proto_backend::proto::ProtoAsset::HandleId(handle_id) => { // panic!("expected `ProtoAsset::AssetPath` in field `{}` of `{}`, but found `ProtoAsset::HandleId`", "preloaded_asset", ::core::any::type_name::()); // } // } // } // } // const _: () = { // mod Assertions {} // }; // }; // -----------------------------------------------------------------------