From f69f79b0090be87449ec87674745685ad57e892d Mon Sep 17 00:00:00 2001 From: Frizi Date: Tue, 25 May 2021 12:40:53 +0200 Subject: [PATCH 01/22] implement and require derive(Component) --- crates/bevy_app/src/app_builder.rs | 12 +- crates/bevy_asset/src/handle.rs | 4 +- crates/bevy_asset/src/loader.rs | 13 +-- crates/bevy_core/src/label.rs | 3 +- crates/bevy_core/src/name.rs | 4 +- crates/bevy_core/src/time/stopwatch.rs | 4 +- crates/bevy_core/src/time/timer.rs | 4 +- crates/bevy_ecs/macros/src/component.rs | 20 ++++ crates/bevy_ecs/macros/src/lib.rs | 9 +- crates/bevy_ecs/src/change_detection.rs | 10 +- crates/bevy_ecs/src/component/mod.rs | 28 ++++- crates/bevy_ecs/src/event.rs | 17 ++- crates/bevy_ecs/src/lib.rs | 11 +- crates/bevy_ecs/src/query/fetch.rs | 6 +- crates/bevy_ecs/src/query/filter.rs | 34 +++--- crates/bevy_ecs/src/query/mod.rs | 7 +- crates/bevy_ecs/src/schedule/stage.rs | 3 + crates/bevy_ecs/src/schedule/state.rs | 19 ++- crates/bevy_ecs/src/schedule/system_set.rs | 16 +-- crates/bevy_ecs/src/system/commands.rs | 60 +++++++--- crates/bevy_ecs/src/system/mod.rs | 10 +- crates/bevy_ecs/src/system/query.rs | 23 +++- crates/bevy_ecs/src/system/system_param.rs | 77 ++++++------ crates/bevy_ecs/src/world/mod.rs | 110 ++++++++++++------ crates/bevy_ecs/src/world/world_cell.rs | 6 +- crates/bevy_pbr/src/light.rs | 6 +- crates/bevy_pbr/src/material.rs | 3 +- .../bevy_render/src/camera/active_cameras.rs | 3 +- crates/bevy_render/src/camera/camera.rs | 2 +- crates/bevy_render/src/camera/projection.rs | 6 +- .../src/camera/visible_entities.rs | 8 +- crates/bevy_render/src/draw.rs | 7 +- .../src/pipeline/render_pipelines.rs | 3 +- crates/bevy_render/src/render_graph/base.rs | 4 +- .../nodes/render_resources_node.rs | 5 +- crates/bevy_render/src/shader/shader_defs.rs | 3 +- crates/bevy_render/src/wireframe/mod.rs | 3 +- crates/bevy_sprite/src/sprite.rs | 3 +- crates/bevy_sprite/src/texture_atlas.rs | 3 +- crates/bevy_text/src/text.rs | 5 +- .../bevy_transform/src/components/children.rs | 3 +- .../src/components/global_transform.rs | 4 +- .../bevy_transform/src/components/parent.rs | 5 +- .../src/components/transform.rs | 4 +- .../src/hierarchy/child_builder.rs | 16 ++- .../bevy_transform/src/hierarchy/hierarchy.rs | 37 +++--- crates/bevy_ui/src/focus.rs | 5 +- crates/bevy_ui/src/ui_node.rs | 8 +- crates/bevy_ui/src/update.rs | 46 ++++---- crates/bevy_ui/src/widget/button.rs | 4 +- crates/bevy_ui/src/widget/image.rs | 3 +- examples/2d/contributors.rs | 4 + examples/2d/many_sprites.rs | 2 + examples/2d/text2d.rs | 3 + examples/3d/load_gltf.rs | 1 + examples/3d/parenting.rs | 1 + examples/3d/render_to_texture.rs | 3 + examples/3d/update_gltf_scene.rs | 1 + examples/3d/z_sort_debug.rs | 1 + examples/ecs/change_detection.rs | 2 +- examples/ecs/ecs_guide.rs | 2 + examples/ecs/iter_combinations.rs | 6 +- examples/ecs/parallel_query.rs | 1 + examples/ecs/query_bundle.rs | 4 +- examples/ecs/removal_detection.rs | 1 + examples/ecs/system_param.rs | 2 + examples/game/breakout.rs | 11 +- examples/scene/scene.rs | 4 +- examples/shader/animate_shader.rs | 2 +- examples/shader/array_texture.rs | 2 +- examples/tools/bevymark.rs | 1 + examples/ui/text.rs | 2 + examples/ui/text_debug.rs | 1 + tests/how_to_test_systems.rs | 2 +- 74 files changed, 489 insertions(+), 279 deletions(-) create mode 100644 crates/bevy_ecs/macros/src/component.rs diff --git a/crates/bevy_app/src/app_builder.rs b/crates/bevy_app/src/app_builder.rs index 58a2dbdd87c22..58bf336223d06 100644 --- a/crates/bevy_app/src/app_builder.rs +++ b/crates/bevy_app/src/app_builder.rs @@ -4,12 +4,12 @@ use crate::{ CoreStage, PluginGroup, PluginGroupBuilder, StartupStage, }; use bevy_ecs::{ - component::{Component, ComponentDescriptor}, + component::ComponentDescriptor, event::Events, schedule::{ RunOnce, Schedule, Stage, StageLabel, State, SystemDescriptor, SystemSet, SystemStage, }, - system::{IntoExclusiveSystem, IntoSystem}, + system::{IntoExclusiveSystem, IntoSystem, Resource}, world::{FromWorld, World}, }; use bevy_utils::tracing::debug; @@ -252,7 +252,7 @@ impl AppBuilder { /// adding [State::get_driver] to additional stages you need it in. pub fn add_state(&mut self, initial: T) -> &mut Self where - T: Component + Debug + Clone + Eq + Hash, + T: Resource + Debug + Clone + Eq + Hash, { self.add_state_to_stage(CoreStage::Update, initial) } @@ -264,7 +264,7 @@ impl AppBuilder { /// stages you need it in. pub fn add_state_to_stage(&mut self, stage: impl StageLabel, initial: T) -> &mut Self where - T: Component + Debug + Clone + Eq + Hash, + T: Resource + Debug + Clone + Eq + Hash, { self.insert_resource(State::new(initial)) .add_system_set_to_stage(stage, State::::get_driver()) @@ -292,7 +292,7 @@ impl AppBuilder { /// and inserting a `Events::::update_system` system into `CoreStage::First`. pub fn add_event(&mut self) -> &mut Self where - T: Component, + T: Resource, { self.insert_resource(Events::::default()) .add_system_to_stage(CoreStage::First, Events::::update_system.system()) @@ -318,7 +318,7 @@ impl AppBuilder { /// ``` pub fn insert_resource(&mut self, resource: T) -> &mut Self where - T: Component, + T: Resource, { self.app.world.insert_resource(resource); self diff --git a/crates/bevy_asset/src/handle.rs b/crates/bevy_asset/src/handle.rs index ae2bb45c0837c..1a606c1a62c80 100644 --- a/crates/bevy_asset/src/handle.rs +++ b/crates/bevy_asset/src/handle.rs @@ -9,7 +9,7 @@ use crate::{ path::{AssetPath, AssetPathId}, Asset, Assets, }; -use bevy_ecs::reflect::ReflectComponent; +use bevy_ecs::{component::Component, reflect::ReflectComponent}; use bevy_reflect::{Reflect, ReflectDeserialize}; use bevy_utils::Uuid; use crossbeam_channel::{Receiver, Sender}; @@ -58,7 +58,7 @@ impl HandleId { /// /// Handles contain a unique id that corresponds to a specific asset in the [Assets](crate::Assets) /// collection. -#[derive(Reflect)] +#[derive(Component, Reflect)] #[reflect(Component)] pub struct Handle where diff --git a/crates/bevy_asset/src/loader.rs b/crates/bevy_asset/src/loader.rs index 825eab38a459e..2eb85782205e4 100644 --- a/crates/bevy_asset/src/loader.rs +++ b/crates/bevy_asset/src/loader.rs @@ -3,10 +3,7 @@ use crate::{ RefChangeChannel, }; use anyhow::Result; -use bevy_ecs::{ - component::Component, - system::{Res, ResMut}, -}; +use bevy_ecs::system::{Res, ResMut}; use bevy_reflect::{TypeUuid, TypeUuidDynamic}; use bevy_tasks::TaskPool; use bevy_utils::{BoxedFuture, HashMap}; @@ -146,7 +143,7 @@ impl<'a> LoadContext<'a> { /// The result of loading an asset of type `T` #[derive(Debug)] -pub struct AssetResult { +pub struct AssetResult { pub asset: Box, pub id: HandleId, pub version: usize, @@ -154,12 +151,12 @@ pub struct AssetResult { /// A channel to send and receive [AssetResult]s #[derive(Debug)] -pub struct AssetLifecycleChannel { +pub struct AssetLifecycleChannel { pub sender: Sender>, pub receiver: Receiver>, } -pub enum AssetLifecycleEvent { +pub enum AssetLifecycleEvent { Create(AssetResult), Free(HandleId), } @@ -193,7 +190,7 @@ impl AssetLifecycle for AssetLifecycleChannel { } } -impl Default for AssetLifecycleChannel { +impl Default for AssetLifecycleChannel { fn default() -> Self { let (sender, receiver) = crossbeam_channel::unbounded(); AssetLifecycleChannel { sender, receiver } diff --git a/crates/bevy_core/src/label.rs b/crates/bevy_core/src/label.rs index ce7b676eb641b..ef6872045b127 100644 --- a/crates/bevy_core/src/label.rs +++ b/crates/bevy_core/src/label.rs @@ -1,4 +1,5 @@ use bevy_ecs::{ + component::Component, entity::Entity, query::Changed, reflect::ReflectComponent, @@ -13,7 +14,7 @@ use std::{ }; /// A collection of labels -#[derive(Default, Reflect)] +#[derive(Component, Default, Reflect)] #[reflect(Component)] pub struct Labels { labels: HashSet>, diff --git a/crates/bevy_core/src/name.rs b/crates/bevy_core/src/name.rs index 7390fd8b18688..593c7ae202baf 100644 --- a/crates/bevy_core/src/name.rs +++ b/crates/bevy_core/src/name.rs @@ -1,4 +1,4 @@ -use bevy_ecs::reflect::ReflectComponent; +use bevy_ecs::{component::Component, reflect::ReflectComponent}; use bevy_reflect::Reflect; use bevy_utils::AHasher; use std::{ @@ -8,7 +8,7 @@ use std::{ }; /// Component used to identify an entity. Stores a hash for faster comparisons -#[derive(Debug, Clone, Reflect)] +#[derive(Component, Debug, Clone, Reflect)] #[reflect(Component)] pub struct Name { hash: u64, // TODO: Shouldn't be serialized diff --git a/crates/bevy_core/src/time/stopwatch.rs b/crates/bevy_core/src/time/stopwatch.rs index 8aa1e974c9647..d03da20824f39 100644 --- a/crates/bevy_core/src/time/stopwatch.rs +++ b/crates/bevy_core/src/time/stopwatch.rs @@ -1,4 +1,4 @@ -use bevy_ecs::reflect::ReflectComponent; +use bevy_ecs::{component::Component, reflect::ReflectComponent}; use bevy_reflect::Reflect; use bevy_utils::Duration; @@ -23,7 +23,7 @@ use bevy_utils::Duration; /// assert!(stopwatch.paused()); /// assert_eq!(stopwatch.elapsed_secs(), 0.0); /// ``` -#[derive(Clone, Debug, Default, Reflect)] +#[derive(Component, Clone, Debug, Default, Reflect)] #[reflect(Component)] pub struct Stopwatch { elapsed: Duration, diff --git a/crates/bevy_core/src/time/timer.rs b/crates/bevy_core/src/time/timer.rs index 290d73e7c197f..041f712b818e2 100644 --- a/crates/bevy_core/src/time/timer.rs +++ b/crates/bevy_core/src/time/timer.rs @@ -1,5 +1,5 @@ use crate::Stopwatch; -use bevy_ecs::reflect::ReflectComponent; +use bevy_ecs::{component::Component, reflect::ReflectComponent}; use bevy_reflect::Reflect; use bevy_utils::Duration; @@ -10,7 +10,7 @@ use bevy_utils::Duration; /// exceeded, and can still be reset at any given point. /// /// Paused timers will not have elapsed time increased. -#[derive(Clone, Debug, Default, Reflect)] +#[derive(Component, Clone, Debug, Default, Reflect)] #[reflect(Component)] pub struct Timer { stopwatch: Stopwatch, diff --git a/crates/bevy_ecs/macros/src/component.rs b/crates/bevy_ecs/macros/src/component.rs new file mode 100644 index 0000000000000..e0aae6e6b7f83 --- /dev/null +++ b/crates/bevy_ecs/macros/src/component.rs @@ -0,0 +1,20 @@ +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, parse_quote, DeriveInput, Path}; + +pub fn derive_component(input: TokenStream) -> TokenStream { + let mut ast = parse_macro_input!(input as DeriveInput); + let bevy_ecs_path: Path = crate::bevy_ecs_path(); + + ast.generics + .make_where_clause() + .predicates + .push(parse_quote! { Self: Send + Sync + 'static }); + + let struct_name = &ast.ident; + let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl(); + + TokenStream::from(quote! { + impl #impl_generics #bevy_ecs_path::component::Component for #struct_name #type_generics #where_clause {} + }) +} diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index 27383ff71ee55..2de0b9e22762b 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -1,5 +1,7 @@ extern crate proc_macro; +mod component; + use bevy_macro_utils::BevyManifest; use proc_macro::TokenStream; use proc_macro2::{Span, TokenStream as TokenStream2}; @@ -475,6 +477,11 @@ fn derive_label(input: DeriveInput, label_type: Ident) -> TokenStream2 { } } -fn bevy_ecs_path() -> syn::Path { +pub(crate) fn bevy_ecs_path() -> syn::Path { BevyManifest::default().get_path("bevy_ecs") } + +#[proc_macro_derive(Component)] +pub fn derive_component(input: TokenStream) -> TokenStream { + component::derive_component(input) +} diff --git a/crates/bevy_ecs/src/change_detection.rs b/crates/bevy_ecs/src/change_detection.rs index bc420879f2db4..087ead17cb782 100644 --- a/crates/bevy_ecs/src/change_detection.rs +++ b/crates/bevy_ecs/src/change_detection.rs @@ -1,4 +1,4 @@ -use crate::component::{Component, ComponentTicks}; +use crate::{component::ComponentTicks, system::Resource}; use bevy_reflect::Reflect; use std::ops::{Deref, DerefMut}; @@ -141,14 +141,14 @@ pub(crate) struct Ticks<'a> { /// Panics when used as a [`SystemParameter`](crate::system::SystemParam) if the resource does not exist. /// /// Use `Option>` instead if the resource might not always exist. -pub struct ResMut<'a, T: Component> { +pub struct ResMut<'a, T: Resource> { pub(crate) value: &'a mut T, pub(crate) ticks: Ticks<'a>, } -change_detection_impl!(ResMut<'a, T>, T, Component); -impl_into_inner!(ResMut<'a, T>, T, Component); -impl_debug!(ResMut<'a, T>, Component); +change_detection_impl!(ResMut<'a, T>, T, Resource); +impl_into_inner!(ResMut<'a, T>, T, Resource); +impl_debug!(ResMut<'a, T>, Resource); /// Unique mutable borrow of an entity's component pub struct Mut<'a, T> { diff --git a/crates/bevy_ecs/src/component/mod.rs b/crates/bevy_ecs/src/component/mod.rs index 4a392bbe3a898..d3cd8c38c56b3 100644 --- a/crates/bevy_ecs/src/component/mod.rs +++ b/crates/bevy_ecs/src/component/mod.rs @@ -2,7 +2,8 @@ mod type_info; pub use type_info::*; -use crate::storage::SparseSetIndex; +use crate::{storage::SparseSetIndex, system::Resource}; +pub use bevy_ecs_macros::Component; use std::{ alloc::Layout, any::{Any, TypeId}, @@ -13,7 +14,7 @@ use thiserror::Error; /// A component is data associated with an [`Entity`](crate::entity::Entity). Each entity can have /// multiple different types of components, but only one of them per type. /// -/// Any type that is `Send + Sync + 'static` automatically implements `Component`. +/// Any type that is `Send + Sync + 'static` can implement `Component` using `#[derive(Component)]`. /// /// Components are added with new entities using [`Commands::spawn`](crate::system::Commands::spawn), /// or to existing entities with [`EntityCommands::insert`](crate::system::EntityCommands::insert), @@ -24,7 +25,26 @@ use thiserror::Error; /// /// Components can be grouped together into a [`Bundle`](crate::bundle::Bundle). pub trait Component: Send + Sync + 'static {} -impl Component for T {} + +// ECS dependencies cannot derive Component, so we must implement it manually for relevant structs. +impl Component for bevy_tasks::Task where Self: Send + Sync + 'static {} + +// For our own convinience, let's implement Component for primitives in tests. +// It will eventually be removed, once tests are not using them anymore. +// Consider those impls deprecated. +#[cfg(test)] +mod private_test_component_impls { + use super::Component; + impl Component for &'static str {} + impl Component for usize {} + impl Component for i32 {} + impl Component for u32 {} + impl Component for u64 {} + impl Component for f32 {} + impl Component for f64 {} + impl Component for u8 {} + impl Component for bool {} +} #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum StorageType { @@ -262,7 +282,7 @@ impl Components { } #[inline] - pub fn get_or_insert_resource_id(&mut self) -> ComponentId { + pub fn get_or_insert_resource_id(&mut self) -> ComponentId { self.get_or_insert_resource_with(TypeId::of::(), TypeInfo::of::) } diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index 11de10b34d084..4ab5cdd846072 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -1,8 +1,5 @@ -use crate as bevy_ecs; -use crate::{ - component::Component, - system::{Local, Res, ResMut, SystemParam}, -}; +use crate::system::{Local, Res, ResMut, SystemParam}; +use crate::{self as bevy_ecs, system::Resource}; use bevy_utils::tracing::trace; use std::{ fmt::{self}, @@ -151,18 +148,18 @@ fn map_instance_event(event_instance: &EventInstance) -> &T { /// Reads events of type `T` in order and tracks which events have already been read. #[derive(SystemParam)] -pub struct EventReader<'a, T: Component> { +pub struct EventReader<'a, T: Resource> { last_event_count: Local<'a, (usize, PhantomData)>, events: Res<'a, Events>, } /// Sends events of type `T`. #[derive(SystemParam)] -pub struct EventWriter<'a, T: Component> { +pub struct EventWriter<'a, T: Resource> { events: ResMut<'a, Events>, } -impl<'a, T: Component> EventWriter<'a, T> { +impl<'a, T: Resource> EventWriter<'a, T> { pub fn send(&mut self, event: T) { self.events.send(event); } @@ -252,7 +249,7 @@ fn internal_event_reader<'a, T>( } } -impl<'a, T: Component> EventReader<'a, T> { +impl<'a, T: Resource> EventReader<'a, T> { /// Iterates over the events this EventReader has not seen yet. This updates the EventReader's /// event counter, which means subsequent event reads will not include events that happened /// before now. @@ -269,7 +266,7 @@ impl<'a, T: Component> EventReader<'a, T> { } } -impl Events { +impl Events { /// "Sends" an `event` by writing it to the current event buffer. [EventReader]s can then read /// the event. pub fn send(&mut self, event: T) { diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index a6fdbced0894d..d20deeeb29c14 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -20,6 +20,7 @@ pub mod prelude { pub use crate::{ bundle::Bundle, change_detection::DetectChanges, + component::Component, entity::Entity, event::{EventReader, EventWriter}, query::{Added, ChangeTrackers, Changed, Or, QueryState, With, WithBundle, Without}, @@ -58,12 +59,14 @@ mod tests { }, }; - #[derive(Debug, PartialEq, Eq)] + #[derive(Component, Debug, PartialEq, Eq)] struct A(usize); + #[derive(Component)] struct B(usize); + #[derive(Component)] struct C; - #[derive(Clone, Debug)] + #[derive(Component, Clone, Debug)] struct DropCk(Arc); impl DropCk { fn new_pair() -> (Self, Arc) { @@ -760,7 +763,7 @@ mod tests { let e1 = world.spawn().insert_bundle((A(0), B(0))).id(); let e2 = world.spawn().insert_bundle((A(0), B(0))).id(); let e3 = world.spawn().insert_bundle((A(0), B(0))).id(); - world.spawn().insert_bundle((A(0), B)); + world.spawn().insert_bundle((A(0), B(0))); world.clear_trackers(); @@ -788,7 +791,7 @@ mod tests { assert_eq!(get_filtered::>(&mut world), vec![e3, e1], "changed entities list should not change (although the order will due to archetype moves)"); // spawning a new A entity should not change existing changed state - world.entity_mut(e1).insert_bundle((A(0), B)); + world.entity_mut(e1).insert_bundle((A(0), B(0))); assert_eq!( get_filtered::>(&mut world), vec![e3, e1], diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 2764f897310f2..39d411e332b9c 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -691,12 +691,14 @@ impl<'w, T: Fetch<'w>> Fetch<'w> for OptionFetch { /// # Examples /// /// ``` -/// # use bevy_ecs::system::Query; +/// # use bevy_ecs::component::Component; /// # use bevy_ecs::query::ChangeTrackers; /// # use bevy_ecs::system::IntoSystem; +/// # use bevy_ecs::system::Query; /// # -/// # #[derive(Debug)] +/// # #[derive(Component, Debug)] /// # struct Name {}; +/// # #[derive(Component)] /// # struct Transform {}; /// # /// fn print_moving_objects_system(query: Query<(&Name, ChangeTrackers)>) { diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index cd3d2c9c801c9..05f0befd45134 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -51,12 +51,14 @@ where /// # Examples /// /// ``` -/// # use bevy_ecs::system::Query; +/// # use bevy_ecs::component::Component; /// # use bevy_ecs::query::With; /// # use bevy_ecs::system::IntoSystem; +/// # use bevy_ecs::system::Query; /// # -/// # #[derive(Debug)] -/// # struct IsBeautiful {}; +/// # #[derive(Component)] +/// # struct IsBeautiful; +/// # #[derive(Component)] /// # struct Name { name: &'static str }; /// # /// fn compliment_entity_system(query: Query<&Name, With>) { @@ -170,12 +172,14 @@ impl<'a, T: Component> Fetch<'a> for WithFetch { /// # Examples /// /// ``` -/// # use bevy_ecs::system::Query; +/// # use bevy_ecs::component::Component; /// # use bevy_ecs::query::Without; /// # use bevy_ecs::system::IntoSystem; +/// # use bevy_ecs::system::Query; /// # -/// # #[derive(Debug)] +/// # #[derive(Component)] /// # struct Permit; +/// # #[derive(Component)] /// # struct Name { name: &'static str }; /// # /// fn no_permit_system(query: Query<&Name, Without>) { @@ -393,14 +397,16 @@ impl<'a, T: Bundle> Fetch<'a> for WithBundleFetch { /// # Examples /// /// ``` +/// # use bevy_ecs::component::Component; /// # use bevy_ecs::entity::Entity; -/// # use bevy_ecs::system::Query; -/// # use bevy_ecs::system::IntoSystem; /// # use bevy_ecs::query::Changed; /// # use bevy_ecs::query::Or; +/// # use bevy_ecs::system::IntoSystem; +/// # use bevy_ecs::system::Query; /// # -/// # #[derive(Debug)] +/// # #[derive(Component, Debug)] /// # struct Color {}; +/// # #[derive(Component)] /// # struct Style {}; /// # /// fn print_cool_entity_system(query: Query, Changed