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
@ -3,6 +3,7 @@ extern crate proc_macro;
|
|||||||
mod component;
|
mod component;
|
||||||
mod fetch;
|
mod fetch;
|
||||||
mod set;
|
mod set;
|
||||||
|
mod states;
|
||||||
|
|
||||||
use crate::{fetch::derive_world_query_impl, set::derive_set};
|
use crate::{fetch::derive_world_query_impl, set::derive_set};
|
||||||
use bevy_macro_utils::{derive_boxed_label, get_named_struct_fields, BevyManifest};
|
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 {
|
pub fn derive_component(input: TokenStream) -> TokenStream {
|
||||||
component::derive_component(input)
|
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::system::Resource;
|
||||||
use crate::world::World;
|
use crate::world::World;
|
||||||
|
|
||||||
|
pub use bevy_ecs_macros::States;
|
||||||
|
|
||||||
/// Types that can define world-wide states in a finite-state machine.
|
/// Types that can define world-wide states in a finite-state machine.
|
||||||
///
|
///
|
||||||
/// The [`Default`] trait defines the starting state.
|
/// The [`Default`] trait defines the starting state.
|
||||||
@ -25,7 +27,7 @@ use crate::world::World;
|
|||||||
/// ```rust
|
/// ```rust
|
||||||
/// use bevy_ecs::prelude::States;
|
/// use bevy_ecs::prelude::States;
|
||||||
///
|
///
|
||||||
/// #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)]
|
/// #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]
|
||||||
/// enum GameState {
|
/// enum GameState {
|
||||||
/// #[default]
|
/// #[default]
|
||||||
/// MainMenu,
|
/// MainMenu,
|
||||||
@ -33,14 +35,6 @@ use crate::world::World;
|
|||||||
/// InGame,
|
/// 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 {
|
pub trait States: 'static + Send + Sync + Clone + PartialEq + Eq + Hash + Debug + Default {
|
||||||
type Iter: Iterator<Item = Self>;
|
type Iter: Iterator<Item = Self>;
|
||||||
|
@ -25,21 +25,13 @@ fn main() {
|
|||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Hash, States)]
|
||||||
enum AppState {
|
enum AppState {
|
||||||
#[default]
|
#[default]
|
||||||
Menu,
|
Menu,
|
||||||
InGame,
|
InGame,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl States for AppState {
|
|
||||||
type Iter = std::array::IntoIter<AppState, 2>;
|
|
||||||
|
|
||||||
fn variants() -> Self::Iter {
|
|
||||||
[AppState::Menu, AppState::InGame].into_iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
struct MenuData {
|
struct MenuData {
|
||||||
button_entity: Entity,
|
button_entity: Entity,
|
||||||
|
@ -5,21 +5,13 @@ use std::f32::consts::PI;
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq, Debug, Hash, Default)]
|
#[derive(Clone, Eq, PartialEq, Debug, Hash, Default, States)]
|
||||||
enum GameState {
|
enum GameState {
|
||||||
#[default]
|
#[default]
|
||||||
Playing,
|
Playing,
|
||||||
GameOver,
|
GameOver,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl States for GameState {
|
|
||||||
type Iter = std::array::IntoIter<GameState, 2>;
|
|
||||||
|
|
||||||
fn variants() -> Self::Iter {
|
|
||||||
[GameState::Playing, GameState::GameOver].into_iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
struct BonusSpawnTimer(Timer);
|
struct BonusSpawnTimer(Timer);
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ use bevy::prelude::*;
|
|||||||
const TEXT_COLOR: Color = Color::rgb(0.9, 0.9, 0.9);
|
const TEXT_COLOR: Color = Color::rgb(0.9, 0.9, 0.9);
|
||||||
|
|
||||||
// Enum that will be used as a global state for the game
|
// 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 {
|
enum GameState {
|
||||||
#[default]
|
#[default]
|
||||||
Splash,
|
Splash,
|
||||||
@ -15,14 +15,6 @@ enum GameState {
|
|||||||
Game,
|
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
|
// 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)]
|
#[derive(Resource, Debug, Component, PartialEq, Eq, Clone, Copy)]
|
||||||
enum DisplayQuality {
|
enum DisplayQuality {
|
||||||
@ -312,7 +304,7 @@ mod menu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// State used for the current menu screen
|
// 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 {
|
enum MenuState {
|
||||||
Main,
|
Main,
|
||||||
Settings,
|
Settings,
|
||||||
@ -322,21 +314,6 @@ mod menu {
|
|||||||
Disabled,
|
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
|
// Tag component used to tag entities added on the main menu screen
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
struct OnMainMenuScreen;
|
struct OnMainMenuScreen;
|
||||||
|
Loading…
Reference in New Issue
Block a user