bevy_derive: Add derives for Deref and DerefMut (#4328)
# Objective A common pattern in Rust is the [newtype](https://doc.rust-lang.org/rust-by-example/generics/new_types.html). This is an especially useful pattern in Bevy as it allows us to give common/foreign types different semantics (such as allowing it to implement `Component` or `FromWorld`) or to simply treat them as a "new type" (clever). For example, it allows us to wrap a common `Vec<String>` and do things like: ```rust #[derive(Component)] struct Items(Vec<String>); fn give_sword(query: Query<&mut Items>) { query.single_mut().0.push(String::from("Flaming Poisoning Raging Sword of Doom")); } ``` > We could then define another struct that wraps `Vec<String>` without anything clashing in the query. However, one of the worst parts of this pattern is the ugly `.0` we have to write in order to access the type we actually care about. This is why people often implement `Deref` and `DerefMut` in order to get around this. Since it's such a common pattern, especially for Bevy, it makes sense to add a derive macro to automatically add those implementations. ## Solution Added a derive macro for `Deref` and another for `DerefMut` (both exported into the prelude). This works on all structs (including tuple structs) as long as they only contain a single field: ```rust #[derive(Deref)] struct Foo(String); #[derive(Deref, DerefMut)] struct Bar { name: String, } ``` This allows us to then remove that pesky `.0`: ```rust #[derive(Component, Deref, DerefMut)] struct Items(Vec<String>); fn give_sword(query: Query<&mut Items>) { query.single_mut().push(String::from("Flaming Poisoning Raging Sword of Doom")); } ``` ### Alternatives There are other alternatives to this such as by using the [`derive_more`](https://crates.io/crates/derive_more) crate. However, it doesn't seem like we need an entire crate just yet since we only need `Deref` and `DerefMut` (for now). ### Considerations One thing to consider is that the Rust std library recommends _not_ using `Deref` and `DerefMut` for things like this: "`Deref` should only be implemented for smart pointers to avoid confusion" ([reference](https://doc.rust-lang.org/std/ops/trait.Deref.html)). Personally, I believe it makes sense to use it in the way described above, but others may disagree. ### Additional Context Discord: https://discord.com/channels/691052431525675048/692572690833473578/956648422163746827 (controversiality discussed [here](https://discord.com/channels/691052431525675048/692572690833473578/956711911481835630)) --- ## Changelog - Add `Deref` derive macro (exported to prelude) - Add `DerefMut` derive macro (exported to prelude) - Updated most newtypes in examples to use one or both derives Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
This commit is contained in:
parent
28ba87e6c8
commit
f16768d868
69
crates/bevy_derive/src/derefs.rs
Normal file
69
crates/bevy_derive/src/derefs.rs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
use proc_macro::{Span, TokenStream};
|
||||||
|
use quote::quote;
|
||||||
|
use syn::{parse_macro_input, Data, DeriveInput, Index, Member, Type};
|
||||||
|
|
||||||
|
pub fn derive_deref(input: TokenStream) -> TokenStream {
|
||||||
|
let ast = parse_macro_input!(input as DeriveInput);
|
||||||
|
|
||||||
|
let ident = &ast.ident;
|
||||||
|
let (field_member, field_type) = match get_inner_field(&ast, false) {
|
||||||
|
Ok(items) => items,
|
||||||
|
Err(err) => {
|
||||||
|
return err.into_compile_error().into();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
|
||||||
|
|
||||||
|
TokenStream::from(quote! {
|
||||||
|
impl #impl_generics ::std::ops::Deref for #ident #ty_generics #where_clause {
|
||||||
|
type Target = #field_type;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.#field_member
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn derive_deref_mut(input: TokenStream) -> TokenStream {
|
||||||
|
let ast = parse_macro_input!(input as DeriveInput);
|
||||||
|
|
||||||
|
let ident = &ast.ident;
|
||||||
|
let (field_member, _) = match get_inner_field(&ast, true) {
|
||||||
|
Ok(items) => items,
|
||||||
|
Err(err) => {
|
||||||
|
return err.into_compile_error().into();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
|
||||||
|
|
||||||
|
TokenStream::from(quote! {
|
||||||
|
impl #impl_generics ::std::ops::DerefMut for #ident #ty_generics #where_clause {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.#field_member
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_inner_field(ast: &DeriveInput, is_mut: bool) -> syn::Result<(Member, &Type)> {
|
||||||
|
match &ast.data {
|
||||||
|
Data::Struct(data_struct) if data_struct.fields.len() == 1 => {
|
||||||
|
let field = data_struct.fields.iter().next().unwrap();
|
||||||
|
let member = field
|
||||||
|
.ident
|
||||||
|
.as_ref()
|
||||||
|
.map(|name| Member::Named(name.clone()))
|
||||||
|
.unwrap_or_else(|| Member::Unnamed(Index::from(0)));
|
||||||
|
Ok((member, &field.ty))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let msg = if is_mut {
|
||||||
|
"DerefMut can only be derived for structs with a single field"
|
||||||
|
} else {
|
||||||
|
"Deref can only be derived for structs with a single field"
|
||||||
|
};
|
||||||
|
Err(syn::Error::new(Span::call_site().into(), msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@ extern crate proc_macro;
|
|||||||
|
|
||||||
mod app_plugin;
|
mod app_plugin;
|
||||||
mod bevy_main;
|
mod bevy_main;
|
||||||
|
mod derefs;
|
||||||
mod enum_variant_meta;
|
mod enum_variant_meta;
|
||||||
mod modules;
|
mod modules;
|
||||||
|
|
||||||
@ -15,6 +16,61 @@ pub fn derive_dynamic_plugin(input: TokenStream) -> TokenStream {
|
|||||||
app_plugin::derive_dynamic_plugin(input)
|
app_plugin::derive_dynamic_plugin(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Implements [`Deref`] for _single-item_ structs. This is especially useful when
|
||||||
|
/// utilizing the [newtype] pattern.
|
||||||
|
///
|
||||||
|
/// If you need [`DerefMut`] as well, consider using the other [derive] macro alongside
|
||||||
|
/// this one.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use bevy_derive::Deref;
|
||||||
|
///
|
||||||
|
/// #[derive(Deref)]
|
||||||
|
/// struct MyNewtype(String);
|
||||||
|
///
|
||||||
|
/// let foo = MyNewtype(String::from("Hello"));
|
||||||
|
/// assert_eq!(5, foo.len());
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`Deref`]: std::ops::Deref
|
||||||
|
/// [newtype]: https://doc.rust-lang.org/rust-by-example/generics/new_types.html
|
||||||
|
/// [`DerefMut`]: std::ops::DerefMut
|
||||||
|
/// [derive]: crate::derive_deref_mut
|
||||||
|
#[proc_macro_derive(Deref)]
|
||||||
|
pub fn derive_deref(input: TokenStream) -> TokenStream {
|
||||||
|
derefs::derive_deref(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements [`DerefMut`] for _single-item_ structs. This is especially useful when
|
||||||
|
/// utilizing the [newtype] pattern.
|
||||||
|
///
|
||||||
|
/// [`DerefMut`] requires a [`Deref`] implementation. You can implement it manually or use
|
||||||
|
/// Bevy's [derive] macro for convenience.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use bevy_derive::{Deref, DerefMut};
|
||||||
|
///
|
||||||
|
/// #[derive(Deref, DerefMut)]
|
||||||
|
/// struct MyNewtype(String);
|
||||||
|
///
|
||||||
|
/// let mut foo = MyNewtype(String::from("Hello"));
|
||||||
|
/// foo.push_str(" World!");
|
||||||
|
/// assert_eq!("Hello World!", *foo);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`DerefMut`]: std::ops::DerefMut
|
||||||
|
/// [newtype]: https://doc.rust-lang.org/rust-by-example/generics/new_types.html
|
||||||
|
/// [`Deref`]: std::ops::Deref
|
||||||
|
/// [derive]: crate::derive_deref
|
||||||
|
#[proc_macro_derive(DerefMut)]
|
||||||
|
pub fn derive_deref_mut(input: TokenStream) -> TokenStream {
|
||||||
|
derefs::derive_deref_mut(input)
|
||||||
|
}
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn bevy_main(attr: TokenStream, item: TokenStream) -> TokenStream {
|
pub fn bevy_main(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
bevy_main::bevy_main(attr, item)
|
bevy_main::bevy_main(attr, item)
|
||||||
|
|||||||
@ -5,7 +5,7 @@ pub use crate::{
|
|||||||
transform::prelude::*, utils::prelude::*, window::prelude::*, DefaultPlugins, MinimalPlugins,
|
transform::prelude::*, utils::prelude::*, window::prelude::*, DefaultPlugins, MinimalPlugins,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use bevy_derive::bevy_main;
|
pub use bevy_derive::{bevy_main, Deref, DerefMut};
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[cfg(feature = "bevy_audio")]
|
#[cfg(feature = "bevy_audio")]
|
||||||
|
|||||||
@ -27,6 +27,7 @@ struct ContributorSelection {
|
|||||||
idx: usize,
|
idx: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deref, DerefMut)]
|
||||||
struct SelectTimer(Timer);
|
struct SelectTimer(Timer);
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
@ -161,7 +162,7 @@ fn select_system(
|
|||||||
mut query: Query<(&Contributor, &mut Sprite, &mut Transform)>,
|
mut query: Query<(&Contributor, &mut Sprite, &mut Transform)>,
|
||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
) {
|
) {
|
||||||
if !timer.0.tick(time.delta()).just_finished() {
|
if !timer.tick(time.delta()).just_finished() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -74,6 +74,7 @@ fn move_camera(time: Res<Time>, mut camera_query: Query<&mut Transform, With<Cam
|
|||||||
* Transform::from_translation(Vec3::X * CAMERA_SPEED * time.delta_seconds());
|
* Transform::from_translation(Vec3::X * CAMERA_SPEED * time.delta_seconds());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deref, DerefMut)]
|
||||||
struct PrintingTimer(Timer);
|
struct PrintingTimer(Timer);
|
||||||
|
|
||||||
impl Default for PrintingTimer {
|
impl Default for PrintingTimer {
|
||||||
@ -84,9 +85,9 @@ impl Default for PrintingTimer {
|
|||||||
|
|
||||||
// System for printing the number of sprites on every tick of the timer
|
// System for printing the number of sprites on every tick of the timer
|
||||||
fn print_sprite_count(time: Res<Time>, mut timer: Local<PrintingTimer>, sprites: Query<&Sprite>) {
|
fn print_sprite_count(time: Res<Time>, mut timer: Local<PrintingTimer>, sprites: Query<&Sprite>) {
|
||||||
timer.0.tick(time.delta());
|
timer.tick(time.delta());
|
||||||
|
|
||||||
if timer.0.just_finished() {
|
if timer.just_finished() {
|
||||||
info!("Sprites: {}", sprites.iter().count(),);
|
info!("Sprites: {}", sprites.iter().count(),);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ fn main() {
|
|||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component, Deref, DerefMut)]
|
||||||
struct AnimationTimer(Timer);
|
struct AnimationTimer(Timer);
|
||||||
|
|
||||||
fn animate_sprite(
|
fn animate_sprite(
|
||||||
@ -21,8 +21,8 @@ fn animate_sprite(
|
|||||||
)>,
|
)>,
|
||||||
) {
|
) {
|
||||||
for (mut timer, mut sprite, texture_atlas_handle) in query.iter_mut() {
|
for (mut timer, mut sprite, texture_atlas_handle) in query.iter_mut() {
|
||||||
timer.0.tick(time.delta());
|
timer.tick(time.delta());
|
||||||
if timer.0.just_finished() {
|
if timer.just_finished() {
|
||||||
let texture_atlas = texture_atlases.get(texture_atlas_handle).unwrap();
|
let texture_atlas = texture_atlases.get(texture_atlas_handle).unwrap();
|
||||||
sprite.index = (sprite.index + 1) % texture_atlas.textures.len();
|
sprite.index = (sprite.index + 1) % texture_atlas.textures.len();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -145,9 +145,9 @@ fn print_mesh_count(
|
|||||||
mut timer: Local<PrintingTimer>,
|
mut timer: Local<PrintingTimer>,
|
||||||
sprites: Query<(&Handle<Mesh>, &ComputedVisibility)>,
|
sprites: Query<(&Handle<Mesh>, &ComputedVisibility)>,
|
||||||
) {
|
) {
|
||||||
timer.0.tick(time.delta());
|
timer.tick(time.delta());
|
||||||
|
|
||||||
if timer.0.just_finished() {
|
if timer.just_finished() {
|
||||||
info!(
|
info!(
|
||||||
"Meshes: {} - Visible Meshes {}",
|
"Meshes: {} - Visible Meshes {}",
|
||||||
sprites.iter().len(),
|
sprites.iter().len(),
|
||||||
@ -156,6 +156,7 @@ fn print_mesh_count(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deref, DerefMut)]
|
||||||
struct PrintingTimer(Timer);
|
struct PrintingTimer(Timer);
|
||||||
|
|
||||||
impl Default for PrintingTimer {
|
impl Default for PrintingTimer {
|
||||||
|
|||||||
@ -22,7 +22,9 @@ fn main() {
|
|||||||
// Number of cubes to spawn across the x, y, and z axis
|
// Number of cubes to spawn across the x, y, and z axis
|
||||||
const NUM_CUBES: u32 = 6;
|
const NUM_CUBES: u32 = 6;
|
||||||
|
|
||||||
|
#[derive(Deref)]
|
||||||
struct BoxMeshHandle(Handle<Mesh>);
|
struct BoxMeshHandle(Handle<Mesh>);
|
||||||
|
#[derive(Deref)]
|
||||||
struct BoxMaterialHandle(Handle<StandardMaterial>);
|
struct BoxMaterialHandle(Handle<StandardMaterial>);
|
||||||
|
|
||||||
/// Startup system which runs only once and generates our Box Mesh
|
/// Startup system which runs only once and generates our Box Mesh
|
||||||
@ -84,8 +86,8 @@ fn handle_tasks(
|
|||||||
if let Some(transform) = future::block_on(future::poll_once(&mut *task)) {
|
if let Some(transform) = future::block_on(future::poll_once(&mut *task)) {
|
||||||
// Add our new PbrBundle of components to our tagged entity
|
// Add our new PbrBundle of components to our tagged entity
|
||||||
commands.entity(entity).insert_bundle(PbrBundle {
|
commands.entity(entity).insert_bundle(PbrBundle {
|
||||||
mesh: box_mesh_handle.0.clone(),
|
mesh: box_mesh_handle.clone(),
|
||||||
material: box_material_handle.0.clone(),
|
material: box_material_handle.clone(),
|
||||||
transform,
|
transform,
|
||||||
..default()
|
..default()
|
||||||
});
|
});
|
||||||
|
|||||||
@ -15,9 +15,11 @@ fn main() {
|
|||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deref)]
|
||||||
struct StreamReceiver(Receiver<u32>);
|
struct StreamReceiver(Receiver<u32>);
|
||||||
struct StreamEvent(u32);
|
struct StreamEvent(u32);
|
||||||
|
|
||||||
|
#[derive(Deref)]
|
||||||
struct LoadedFont(Handle<Font>);
|
struct LoadedFont(Handle<Font>);
|
||||||
|
|
||||||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
@ -43,7 +45,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||||||
|
|
||||||
// This system reads from the receiver and sends events to Bevy
|
// This system reads from the receiver and sends events to Bevy
|
||||||
fn read_stream(receiver: ResMut<StreamReceiver>, mut events: EventWriter<StreamEvent>) {
|
fn read_stream(receiver: ResMut<StreamReceiver>, mut events: EventWriter<StreamEvent>) {
|
||||||
for from_stream in receiver.0.try_iter() {
|
for from_stream in receiver.try_iter() {
|
||||||
events.send(StreamEvent(from_stream));
|
events.send(StreamEvent(from_stream));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -54,7 +56,7 @@ fn spawn_text(
|
|||||||
loaded_font: Res<LoadedFont>,
|
loaded_font: Res<LoadedFont>,
|
||||||
) {
|
) {
|
||||||
let text_style = TextStyle {
|
let text_style = TextStyle {
|
||||||
font: loaded_font.0.clone(),
|
font: loaded_font.clone(),
|
||||||
font_size: 20.0,
|
font_size: 20.0,
|
||||||
color: Color::WHITE,
|
color: Color::WHITE,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -20,7 +20,7 @@ enum AppState {
|
|||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
struct TextToPrint(String);
|
struct TextToPrint(String);
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component, Deref, DerefMut)]
|
||||||
struct PrinterTick(bevy::prelude::Timer);
|
struct PrinterTick(bevy::prelude::Timer);
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
@ -67,7 +67,7 @@ fn setup_system(mut commands: Commands) {
|
|||||||
|
|
||||||
fn print_text_system(time: Res<Time>, mut query: Query<(&mut PrinterTick, &TextToPrint)>) {
|
fn print_text_system(time: Res<Time>, mut query: Query<(&mut PrinterTick, &TextToPrint)>) {
|
||||||
for (mut timer, text) in query.iter_mut() {
|
for (mut timer, text) in query.iter_mut() {
|
||||||
if timer.0.tick(time.delta()).just_finished() {
|
if timer.tick(time.delta()).just_finished() {
|
||||||
info!("{}", text.0);
|
info!("{}", text.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use bevy::{prelude::*, tasks::prelude::*};
|
use bevy::{prelude::*, tasks::prelude::*};
|
||||||
use rand::random;
|
use rand::random;
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component, Deref)]
|
||||||
struct Velocity(Vec2);
|
struct Velocity(Vec2);
|
||||||
|
|
||||||
fn spawn_system(mut commands: Commands, asset_server: Res<AssetServer>) {
|
fn spawn_system(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
@ -31,7 +31,7 @@ fn move_system(pool: Res<ComputeTaskPool>, mut sprites: Query<(&mut Transform, &
|
|||||||
// See the ParallelIterator documentation for more information on when
|
// See the ParallelIterator documentation for more information on when
|
||||||
// to use or not use ParallelIterator over a normal Iterator.
|
// to use or not use ParallelIterator over a normal Iterator.
|
||||||
sprites.par_for_each_mut(&pool, 32, |(mut transform, velocity)| {
|
sprites.par_for_each_mut(&pool, 32, |(mut transform, velocity)| {
|
||||||
transform.translation += velocity.0.extend(0.0);
|
transform.translation += velocity.extend(0.0);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,11 +8,12 @@ fn main() {
|
|||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deref)]
|
||||||
struct Message(String);
|
struct Message(String);
|
||||||
|
|
||||||
// this system produces a Result<usize> output by trying to parse the Message resource
|
// this system produces a Result<usize> output by trying to parse the Message resource
|
||||||
fn parse_message_system(message: Res<Message>) -> Result<usize> {
|
fn parse_message_system(message: Res<Message>) -> Result<usize> {
|
||||||
Ok(message.0.parse::<usize>()?)
|
Ok(message.parse::<usize>()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This system takes a Result<usize> input and either prints the parsed value or the error message
|
// This system takes a Result<usize> input and either prints the parsed value or the error message
|
||||||
|
|||||||
@ -10,7 +10,7 @@ fn main() {
|
|||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component, Deref, DerefMut)]
|
||||||
pub struct PrintOnCompletionTimer(Timer);
|
pub struct PrintOnCompletionTimer(Timer);
|
||||||
|
|
||||||
pub struct Countdown {
|
pub struct Countdown {
|
||||||
@ -44,7 +44,7 @@ fn setup(mut commands: Commands) {
|
|||||||
/// using bevy's `Time` resource to get the delta between each update.
|
/// using bevy's `Time` resource to get the delta between each update.
|
||||||
fn print_when_completed(time: Res<Time>, mut query: Query<&mut PrintOnCompletionTimer>) {
|
fn print_when_completed(time: Res<Time>, mut query: Query<&mut PrintOnCompletionTimer>) {
|
||||||
for mut timer in query.iter_mut() {
|
for mut timer in query.iter_mut() {
|
||||||
if timer.0.tick(time.delta()).just_finished() {
|
if timer.tick(time.delta()).just_finished() {
|
||||||
info!("Entity timer just finished");
|
info!("Entity timer just finished");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -76,7 +76,7 @@ struct Paddle;
|
|||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
struct Ball;
|
struct Ball;
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component, Deref, DerefMut)]
|
||||||
struct Velocity(Vec2);
|
struct Velocity(Vec2);
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
@ -334,8 +334,8 @@ fn move_paddle(
|
|||||||
|
|
||||||
fn apply_velocity(mut query: Query<(&mut Transform, &Velocity)>) {
|
fn apply_velocity(mut query: Query<(&mut Transform, &Velocity)>) {
|
||||||
for (mut transform, velocity) in query.iter_mut() {
|
for (mut transform, velocity) in query.iter_mut() {
|
||||||
transform.translation.x += velocity.0.x * TIME_STEP;
|
transform.translation.x += velocity.x * TIME_STEP;
|
||||||
transform.translation.y += velocity.0.y * TIME_STEP;
|
transform.translation.y += velocity.y * TIME_STEP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -375,21 +375,21 @@ fn check_for_collisions(
|
|||||||
// only reflect if the ball's velocity is going in the opposite direction of the
|
// only reflect if the ball's velocity is going in the opposite direction of the
|
||||||
// collision
|
// collision
|
||||||
match collision {
|
match collision {
|
||||||
Collision::Left => reflect_x = ball_velocity.0.x > 0.0,
|
Collision::Left => reflect_x = ball_velocity.x > 0.0,
|
||||||
Collision::Right => reflect_x = ball_velocity.0.x < 0.0,
|
Collision::Right => reflect_x = ball_velocity.x < 0.0,
|
||||||
Collision::Top => reflect_y = ball_velocity.0.y < 0.0,
|
Collision::Top => reflect_y = ball_velocity.y < 0.0,
|
||||||
Collision::Bottom => reflect_y = ball_velocity.0.y > 0.0,
|
Collision::Bottom => reflect_y = ball_velocity.y > 0.0,
|
||||||
Collision::Inside => { /* do nothing */ }
|
Collision::Inside => { /* do nothing */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
// reflect velocity on the x-axis if we hit something on the x-axis
|
// reflect velocity on the x-axis if we hit something on the x-axis
|
||||||
if reflect_x {
|
if reflect_x {
|
||||||
ball_velocity.0.x = -ball_velocity.0.x;
|
ball_velocity.x = -ball_velocity.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
// reflect velocity on the y-axis if we hit something on the y-axis
|
// reflect velocity on the y-axis if we hit something on the y-axis
|
||||||
if reflect_y {
|
if reflect_y {
|
||||||
ball_velocity.0.y = -ball_velocity.0.y;
|
ball_velocity.y = -ball_velocity.y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -76,6 +76,7 @@ mod splash {
|
|||||||
struct OnSplashScreen;
|
struct OnSplashScreen;
|
||||||
|
|
||||||
// Newtype to use a `Timer` for this screen as a resource
|
// Newtype to use a `Timer` for this screen as a resource
|
||||||
|
#[derive(Deref, DerefMut)]
|
||||||
struct SplashTimer(Timer);
|
struct SplashTimer(Timer);
|
||||||
|
|
||||||
fn splash_setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
fn splash_setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
@ -104,7 +105,7 @@ mod splash {
|
|||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
mut timer: ResMut<SplashTimer>,
|
mut timer: ResMut<SplashTimer>,
|
||||||
) {
|
) {
|
||||||
if timer.0.tick(time.delta()).finished() {
|
if timer.tick(time.delta()).finished() {
|
||||||
game_state.set(GameState::Menu).unwrap();
|
game_state.set(GameState::Menu).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -133,6 +134,7 @@ mod game {
|
|||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
struct OnGameScreen;
|
struct OnGameScreen;
|
||||||
|
|
||||||
|
#[derive(Deref, DerefMut)]
|
||||||
struct GameTimer(Timer);
|
struct GameTimer(Timer);
|
||||||
|
|
||||||
fn game_setup(
|
fn game_setup(
|
||||||
@ -227,7 +229,7 @@ mod game {
|
|||||||
mut game_state: ResMut<State<GameState>>,
|
mut game_state: ResMut<State<GameState>>,
|
||||||
mut timer: ResMut<GameTimer>,
|
mut timer: ResMut<GameTimer>,
|
||||||
) {
|
) {
|
||||||
if timer.0.tick(time.delta()).finished() {
|
if timer.tick(time.delta()).finished() {
|
||||||
game_state.set(GameState::Menu).unwrap();
|
game_state.set(GameState::Menu).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -75,11 +75,12 @@ impl Plugin for GameOfLifeComputePlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deref)]
|
||||||
struct GameOfLifeImage(Handle<Image>);
|
struct GameOfLifeImage(Handle<Image>);
|
||||||
struct GameOfLifeImageBindGroup(BindGroup);
|
struct GameOfLifeImageBindGroup(BindGroup);
|
||||||
|
|
||||||
fn extract_game_of_life_image(mut commands: Commands, image: Res<GameOfLifeImage>) {
|
fn extract_game_of_life_image(mut commands: Commands, image: Res<GameOfLifeImage>) {
|
||||||
commands.insert_resource(GameOfLifeImage(image.0.clone()));
|
commands.insert_resource(GameOfLifeImage(image.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn queue_bind_group(
|
fn queue_bind_group(
|
||||||
|
|||||||
@ -62,7 +62,7 @@ fn setup(mut commands: Commands, mut meshes: ResMut<Assets<Mesh>>) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component, Deref)]
|
||||||
struct InstanceMaterialData(Vec<InstanceData>);
|
struct InstanceMaterialData(Vec<InstanceData>);
|
||||||
impl ExtractComponent for InstanceMaterialData {
|
impl ExtractComponent for InstanceMaterialData {
|
||||||
type Query = &'static InstanceMaterialData;
|
type Query = &'static InstanceMaterialData;
|
||||||
@ -151,12 +151,12 @@ fn prepare_instance_buffers(
|
|||||||
for (entity, instance_data) in query.iter() {
|
for (entity, instance_data) in query.iter() {
|
||||||
let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
|
let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
|
||||||
label: Some("instance data buffer"),
|
label: Some("instance data buffer"),
|
||||||
contents: bytemuck::cast_slice(instance_data.0.as_slice()),
|
contents: bytemuck::cast_slice(instance_data.as_slice()),
|
||||||
usage: BufferUsages::VERTEX | BufferUsages::COPY_DST,
|
usage: BufferUsages::VERTEX | BufferUsages::COPY_DST,
|
||||||
});
|
});
|
||||||
commands.entity(entity).insert(InstanceBuffer {
|
commands.entity(entity).insert(InstanceBuffer {
|
||||||
buffer,
|
buffer,
|
||||||
length: instance_data.0.len(),
|
length: instance_data.len(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -73,7 +73,7 @@ fn scheduled_spawner(
|
|||||||
&windows,
|
&windows,
|
||||||
&mut counter,
|
&mut counter,
|
||||||
scheduled.per_wave,
|
scheduled.per_wave,
|
||||||
bird_texture.0.clone_weak(),
|
bird_texture.clone_weak(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
@ -82,6 +82,7 @@ fn scheduled_spawner(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deref)]
|
||||||
struct BirdTexture(Handle<Image>);
|
struct BirdTexture(Handle<Image>);
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
@ -177,7 +178,7 @@ fn mouse_handler(
|
|||||||
&windows,
|
&windows,
|
||||||
&mut counter,
|
&mut counter,
|
||||||
spawn_count,
|
spawn_count,
|
||||||
bird_texture.0.clone_weak(),
|
bird_texture.clone_weak(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user