diff --git a/README.md b/README.md index f8392abe0d422840d1cb7311fd16f6fe94d661c4..1dc30287f597edf5d7fc0391603ff5ae5588774c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Micro Game Macros - - +[](https://docs.rs/micro_games_macros) +[](https://crates.io/crates/micro_games_macros) A collection of utility macros for building games diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000000000000000000000000000000000000..a6986e3a04e432ea56dc45a8cb15b7f05093e00b --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,3 @@ +hard_tabs = true +use_field_init_shorthand = true +use_try_shorthand = true diff --git a/src/lib.rs b/src/lib.rs index d4b5c21763f15f7ca44be76682561d625136f5b5..e58555f28ec3b19bf3237285dd1ee5f904d47714 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,97 @@ +//! A collection of complimentary utility macros for building games +//! +//! Use the [asset_system] macro to create a set of types that manage loading assets, and then +//! derive [JsonLoader] for any asset type to create a flexible resource loader that links in +//! to the generated asset system +//! +//! ```rust +//! use bevy::prelude::{App, DefaultPlugins, Image, Plugin, Res, ResMut, Resource, Assets, TextureAtlas}; +//! use bevy::reflect::{TypePath, TypeUuid}; +//! use micro_games_macros::{asset_system, JsonLoader}; +//! use serde::{Deserialize, Serialize}; +//! +//! // We can customise the properties on the "asset loader" type that the +//! // macro generates by adding "loader_property" annotations. The parameter +//! // to this macro is simple any property declaration that could be added to +//! // a regular type that derives SystemParam (including lifetimes) +//! +//! #[asset_system] +//! #[loader_property(pub sheets: ResMut<'w, Assets<TextureAtlas>>)] +//! pub struct AssetHandles { +//! // For non-JsonLoader assets, you just need to specify a name -> AssetType property. +//! // The generated asset handles type will contain a hashmap of name -> Handle<AssetType> +//! // under the property name specified. Loading functions such as load_asset_name will +//! // be created on the loading type, where the first parameter is the path and the second +//! // parameter is the asset name that will be used in the hashmap for later retrieval of +//! // the asset +//! images: Image, +//! // To use an asset_system with a JsonLoader, you will need to add entries for both the +//! // asset itself and its index type (generated by JsonLoader). Specific instances of +//! // an asset are stored under the Item type (no suffix), keyed in the handle map by their +//! // ID property. The index generated from each file (a map of ID -> Item) is stored under +//! // the Index type (type name: AssetName + Index), keyed by the name provided when loading +//! // the asset +//! some_resource: MyCoolResource, +//! // The property names generate load functions, but don't have to match the name of the +//! // resource being loaded. Index & Item types for a JsonLoader resource can have their +//! // names specified as part of the JsonLoader derive +//! some_index_of_resources: MyCoolResourceIndex, +//! } +//! +//! #[derive(JsonLoader, TypePath, TypeUuid, Serialize, Deserialize)] +//! #[loader(extension = "mcr", uuid = "00000000-0000-0000-0000-000000000000", +//! asset_name = some_resource, index_name = some_index_of_resources)] +//! #[uuid = "10000000-0000-0000-0000-000000000001"] +//! pub struct MyCoolResource { +//! // You must include exactly one of either a property named "id", or another property annotated +//! // with the "asset_id" attribute. +//! #[asset_id] +//! some_identifier: String, +//! foo: usize, +//! bar: bool, +//! } +//! +//! // The asset system generates a system param for scheduling the load of an asset. It uses bevy's +//! // asset server, which makes the system compatible with plugins such as bevy_embedded_assets +//! pub fn loading_system(mut loader: AssetHandlesLoader) { +//! // The loader contains a number of functions for loading either individual assets +//! // (two parameters, path and id), or a series of assets (a single parameter, a vector +//! // of tuples each denoting path and id) +//! loader.load_images("path/to/my_image.png", "my_image"); +//! +//! // JsonLoader assets require the use of the Index type to correctly load them, even for +//! // files that only contain one object instance. The identifier (either id or asset_id) +//! // will be used to add each instance of an asset to the Item type map +//! loader.load_some_index_of_resources("path/to/my_asset.mcr", "my_asset"); +//! } +//! +//! pub fn use_asset_system(assets: Res<AssetHandles>) { +//! // JsonLoader assets can be accessed through the item type functions. These functions +//! // provide a weak handle to the asset, meaning that the assets will usually unload +//! // if removed from the AssetHandles type unless another strong handle was created externally +//! let some_resource_handle = assets.some_resource("the_asset_id"); +//! +//! // The handle functions exactly match the name of the asset property in the originally +//! // defined struct +//! let image_handle = assets.images("my_image"); +//! } +//! +//! pub struct AssetSystemPlugin; +//! impl Plugin for AssetSystemPlugin { +//! fn build(&self, app: &mut App) { +//! app.init_resource::<AssetHandles>() +//! // JsonLoader will create a plugin that wraps all of the functionality for a single +//! // asset type, including loaders and hot reload support +//! .add_plugins(MyCoolResourcePlugin); +//! } +//! } +//! +//! fn main() { +//! let app = App::new() +//! .add_plugins((DefaultPlugins, AssetSystemPlugin)); +//! } +//! ``` + use proc_macro::TokenStream; use syn::{parse_macro_input, DeriveInput}; @@ -42,22 +136,22 @@ pub(crate) mod std_traits; /// /// #[derive(Resource)] /// pub struct AssetHandles { -/// simple_asset: HashMap<String, Handle<SimpleAsset>>, -/// simple_asset_index: HashMap<String, Handle<SimpleAssetIndex>>, +/// simple_asset: HashMap<String, Handle<SimpleAsset>>, +/// simple_asset_index: HashMap<String, Handle<SimpleAssetIndex>>, /// } /// /// #[derive(JsonLoader, TypePath, TypeUuid, Serialize, Deserialize)] /// #[loader(extension = "satt", uuid = "00000000-0000-0000-0000-000000000000")] /// #[uuid = "00000000-0000-0000-0000-000000000001"] /// pub struct SimpleAsset { -/// id: String, -/// widget: usize, +/// id: String, +/// widget: usize, /// } /// /// fn main() { -/// bevy::app::App::new() -/// .add_plugins(bevy::prelude::DefaultPlugins) -/// .add_plugins(SimpleAssetPlugin); +/// bevy::app::App::new() +/// .add_plugins(bevy::prelude::DefaultPlugins) +/// .add_plugins(SimpleAssetPlugin); /// } /// ``` /// @@ -79,26 +173,26 @@ pub(crate) mod std_traits; /// )] /// #[uuid = "00000000-0000-0000-0000-000000000001"] /// pub struct MyAsset { -/// /// The asset identifier needs to implement [std::fmt::Display] -/// #[asset_id] -/// uniq_ident: usize, +/// /// The asset identifier needs to implement [std::fmt::Display] +/// #[asset_id] +/// uniq_ident: usize, /// } /// /// pub mod inner_module { /// # use bevy::prelude::{Handle, Resource}; /// # use std::collections::HashMap; -/// -/// #[derive(Resource)] -/// pub struct SimpleAssetLocator { -/// pub some_asset_prop: HashMap<String, Handle<super::MyAsset>>, -/// pub set_of_assets: HashMap<String, Handle<super::MyAssetIndex>>, -/// } +/// +/// #[derive(Resource)] +/// pub struct SimpleAssetLocator { +/// pub some_asset_prop: HashMap<String, Handle<super::MyAsset>>, +/// pub set_of_assets: HashMap<String, Handle<super::MyAssetIndex>>, +/// } /// } /// /// fn main() { -/// bevy::app::App::new() -/// .add_plugins(bevy::prelude::DefaultPlugins) -/// .add_plugins(MyAssetPlugin); +/// bevy::app::App::new() +/// .add_plugins(bevy::prelude::DefaultPlugins) +/// .add_plugins(MyAssetPlugin); /// } /// ``` #[proc_macro_derive(JsonLoader, attributes(loader, asset_id))] @@ -120,14 +214,14 @@ pub fn json_loader(input: TokenStream) -> TokenStream { /// /// #[asset_system] /// pub struct AssetHandles { -/// my_asset: Image, +/// my_asset: Image, /// } /// /// pub fn loading_system(mut loader: AssetHandlesLoader) { -/// loader.load_my_asset("path/to/asset.png", "Asset"); +/// loader.load_my_asset("path/to/asset.png", "Asset"); /// } /// pub fn use_asset_system(assets: Res<AssetHandles>) { -/// let handle = assets.my_asset("Asset"); +/// let handle = assets.my_asset("Asset"); /// } /// ``` /// @@ -139,17 +233,17 @@ pub fn json_loader(input: TokenStream) -> TokenStream { /// /// #[asset_system] /// pub struct AssetHandles { -/// image: Image, -/// #[skip] -/// spritesheet: TextureAtlas +/// image: Image, +/// #[skip] +/// spritesheet: TextureAtlas, /// } /// -/// impl <'w>AssetHandlesLoader<'w> { -/// pub fn load_spritesheet(&mut self, path: String, name: String) { -/// let image_handle = self.load_image(path, name.clone()); -/// // let sheet_handle = .. more code .. -/// // self.spritesheet.insert(name, sheet_handle); -/// } +/// impl<'w> AssetHandlesLoader<'w> { +/// pub fn load_spritesheet(&mut self, path: String, name: String) { +/// let image_handle = self.load_image(path, name.clone()); +/// // let sheet_handle = .. more code .. +/// // self.spritesheet.insert(name, sheet_handle); +/// } /// } /// ``` /// @@ -157,34 +251,36 @@ pub fn json_loader(input: TokenStream) -> TokenStream { /// The included property must be a `SystemParam`, and has access to the `'w` lifetime /// /// ```rust -/// use bevy::prelude::{EventWriter, Event, Image, TextureAtlas, Assets, ResMut, Vec2, Handle}; +/// use bevy::prelude::{Assets, Event, EventWriter, Handle, Image, ResMut, TextureAtlas, Vec2}; /// use micro_games_macros::{asset_system, loader_property}; /// /// #[derive(Event)] /// pub struct LoadingEvent { -/// pub event_id: usize, +/// pub event_id: usize, /// } /// /// #[asset_system] /// #[loader_property(pub load_events: EventWriter<'w, LoadingEvent>)] /// #[loader_property(pub sheets: ResMut<'w, Assets<TextureAtlas>>)] /// pub struct AssetHandles { -/// image: Image, -/// #[skip] -/// spritesheet: TextureAtlas +/// image: Image, +/// #[skip] +/// spritesheet: TextureAtlas, /// } /// -/// impl <'w>AssetHandlesLoader<'w> { -/// pub fn load_spritesheet(&mut self, path: String, name: String) -> Handle<TextureAtlas> { -/// let image_handle = self.load_image(path, name.clone()); -/// let sheet = TextureAtlas::new_empty(image_handle, Vec2::ZERO); -/// let sheet_handle = self.sheets.add(sheet); -/// -/// self.storage.spritesheet.insert(name.clone(), sheet_handle.clone()); -/// self.load_events.send(LoadingEvent { event_id: 123 }); -/// -/// sheet_handle -/// } +/// impl<'w> AssetHandlesLoader<'w> { +/// pub fn load_spritesheet(&mut self, path: String, name: String) -> Handle<TextureAtlas> { +/// let image_handle = self.load_image(path, name.clone()); +/// let sheet = TextureAtlas::new_empty(image_handle, Vec2::ZERO); +/// let sheet_handle = self.sheets.add(sheet); +/// +/// self.storage +/// .spritesheet +/// .insert(name.clone(), sheet_handle.clone()); +/// self.load_events.send(LoadingEvent { event_id: 123 }); +/// +/// sheet_handle +/// } /// } /// ``` #[proc_macro_attribute] @@ -240,7 +336,7 @@ pub fn derive_kayak_wigdet(input: TokenStream) -> TokenStream { /// /// #[derive(PartialEq, Eq, Debug, FromInner)] /// struct MyValue { -/// foo_bar: usize, +/// foo_bar: usize, /// } /// /// let value = MyValue::from(2000);