mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
States derive macro (#7535)
# Objective Implementing `States` manually is repetitive, so let's not. One thing I'm unsure of is whether the macro import statement is in the right place.
This commit is contained in:
parent
6d399bfaf8
commit
6314f50e7b
6 changed files with 57 additions and 52 deletions
|
@ -3,6 +3,7 @@ extern crate proc_macro;
|
|||
mod component;
|
||||
mod fetch;
|
||||
mod set;
|
||||
mod states;
|
||||
|
||||
use crate::{fetch::derive_world_query_impl, set::derive_set};
|
||||
use bevy_macro_utils::{derive_boxed_label, get_named_struct_fields, BevyManifest};
|
||||
|
@ -558,3 +559,8 @@ pub fn derive_resource(input: TokenStream) -> TokenStream {
|
|||
pub fn derive_component(input: TokenStream) -> TokenStream {
|
||||
component::derive_component(input)
|
||||
}
|
||||
|
||||
#[proc_macro_derive(States)]
|
||||
pub fn derive_states(input: TokenStream) -> TokenStream {
|
||||
states::derive_states(input)
|
||||
}
|
||||
|
|
44
crates/bevy_ecs/macros/src/states.rs
Normal file
44
crates/bevy_ecs/macros/src/states.rs
Normal file
|
@ -0,0 +1,44 @@
|
|||
use proc_macro::{Span, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{parse_macro_input, Data::Enum, DeriveInput};
|
||||
|
||||
use crate::bevy_ecs_path;
|
||||
|
||||
pub fn derive_states(input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as DeriveInput);
|
||||
let error = || {
|
||||
syn::Error::new(
|
||||
Span::call_site().into(),
|
||||
"derive(States) only supports fieldless enums",
|
||||
)
|
||||
.into_compile_error()
|
||||
.into()
|
||||
};
|
||||
let Enum(enumeration) = ast.data else {
|
||||
return error();
|
||||
};
|
||||
if enumeration.variants.iter().any(|v| !v.fields.is_empty()) {
|
||||
return error();
|
||||
}
|
||||
|
||||
let generics = ast.generics;
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
let mut trait_path = bevy_ecs_path();
|
||||
trait_path.segments.push(format_ident!("schedule").into());
|
||||
trait_path.segments.push(format_ident!("States").into());
|
||||
let struct_name = &ast.ident;
|
||||
let idents = enumeration.variants.iter().map(|v| &v.ident);
|
||||
let len = idents.len();
|
||||
|
||||
quote! {
|
||||
impl #impl_generics #trait_path for #struct_name #ty_generics #where_clause {
|
||||
type Iter = std::array::IntoIter<Self, #len>;
|
||||
|
||||
fn variants() -> Self::Iter {
|
||||
[#(Self::#idents,)*].into_iter()
|
||||
}
|
||||
}
|
||||
}
|
||||
.into()
|
||||
}
|
|
@ -7,6 +7,8 @@ use crate::schedule::{ScheduleLabel, SystemSet};
|
|||
use crate::system::Resource;
|
||||
use crate::world::World;
|
||||
|
||||
pub use bevy_ecs_macros::States;
|
||||
|
||||
/// Types that can define world-wide states in a finite-state machine.
|
||||
///
|
||||
/// The [`Default`] trait defines the starting state.
|
||||
|
@ -25,7 +27,7 @@ use crate::world::World;
|
|||
/// ```rust
|
||||
/// use bevy_ecs::prelude::States;
|
||||
///
|
||||
/// #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)]
|
||||
/// #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]
|
||||
/// enum GameState {
|
||||
/// #[default]
|
||||
/// MainMenu,
|
||||
|
@ -33,14 +35,6 @@ use crate::world::World;
|
|||
/// InGame,
|
||||
/// }
|
||||
///
|
||||
/// impl States for GameState {
|
||||
/// type Iter = std::array::IntoIter<GameState, 3>;
|
||||
///
|
||||
/// fn variants() -> Self::Iter {
|
||||
/// [GameState::MainMenu, GameState::SettingsMenu, GameState::InGame].into_iter()
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// ```
|
||||
pub trait States: 'static + Send + Sync + Clone + PartialEq + Eq + Hash + Debug + Default {
|
||||
type Iter: Iterator<Item = Self>;
|
||||
|
|
|
@ -25,21 +25,13 @@ fn main() {
|
|||
.run();
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Hash)]
|
||||
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Hash, States)]
|
||||
enum AppState {
|
||||
#[default]
|
||||
Menu,
|
||||
InGame,
|
||||
}
|
||||
|
||||
impl States for AppState {
|
||||
type Iter = std::array::IntoIter<AppState, 2>;
|
||||
|
||||
fn variants() -> Self::Iter {
|
||||
[AppState::Menu, AppState::InGame].into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
struct MenuData {
|
||||
button_entity: Entity,
|
||||
|
|
|
@ -5,21 +5,13 @@ use std::f32::consts::PI;
|
|||
use bevy::prelude::*;
|
||||
use rand::Rng;
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Hash, Default)]
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Hash, Default, States)]
|
||||
enum GameState {
|
||||
#[default]
|
||||
Playing,
|
||||
GameOver,
|
||||
}
|
||||
|
||||
impl States for GameState {
|
||||
type Iter = std::array::IntoIter<GameState, 2>;
|
||||
|
||||
fn variants() -> Self::Iter {
|
||||
[GameState::Playing, GameState::GameOver].into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
struct BonusSpawnTimer(Timer);
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ use bevy::prelude::*;
|
|||
const TEXT_COLOR: Color = Color::rgb(0.9, 0.9, 0.9);
|
||||
|
||||
// Enum that will be used as a global state for the game
|
||||
#[derive(Clone, Copy, Default, Eq, PartialEq, Debug, Hash)]
|
||||
#[derive(Clone, Copy, Default, Eq, PartialEq, Debug, Hash, States)]
|
||||
enum GameState {
|
||||
#[default]
|
||||
Splash,
|
||||
|
@ -15,14 +15,6 @@ enum GameState {
|
|||
Game,
|
||||
}
|
||||
|
||||
impl States for GameState {
|
||||
type Iter = std::array::IntoIter<GameState, 3>;
|
||||
|
||||
fn variants() -> Self::Iter {
|
||||
[GameState::Splash, GameState::Menu, GameState::Game].into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
// One of the two settings that can be set through the menu. It will be a resource in the app
|
||||
#[derive(Resource, Debug, Component, PartialEq, Eq, Clone, Copy)]
|
||||
enum DisplayQuality {
|
||||
|
@ -312,7 +304,7 @@ mod menu {
|
|||
}
|
||||
|
||||
// State used for the current menu screen
|
||||
#[derive(Clone, Copy, Default, Eq, PartialEq, Debug, Hash)]
|
||||
#[derive(Clone, Copy, Default, Eq, PartialEq, Debug, Hash, States)]
|
||||
enum MenuState {
|
||||
Main,
|
||||
Settings,
|
||||
|
@ -322,21 +314,6 @@ mod menu {
|
|||
Disabled,
|
||||
}
|
||||
|
||||
impl States for MenuState {
|
||||
type Iter = std::array::IntoIter<MenuState, 5>;
|
||||
|
||||
fn variants() -> Self::Iter {
|
||||
[
|
||||
MenuState::Main,
|
||||
MenuState::Settings,
|
||||
MenuState::SettingsDisplay,
|
||||
MenuState::SettingsSound,
|
||||
MenuState::Disabled,
|
||||
]
|
||||
.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
// Tag component used to tag entities added on the main menu screen
|
||||
#[derive(Component)]
|
||||
struct OnMainMenuScreen;
|
||||
|
|
Loading…
Reference in a new issue