diff --git a/Cargo.toml b/Cargo.toml index 95141021a8077..715027509e0ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -259,6 +259,10 @@ path = "examples/ecs/change_detection.rs" name = "event" path = "examples/ecs/event.rs" +[[example]] +name = "per_entity_events" +path = "examples/ecs/per_entity_events.rs" + [[example]] name = "fixed_timestep" path = "examples/ecs/fixed_timestep.rs" diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index 889d9d22e8b4c..26024e2ffe063 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -1,7 +1,6 @@ -use crate as bevy_ecs; use crate::{ component::Component, - system::{Local, Res, ResMut, SystemParam}, + system::{LocalState, Query, ResMut, ResMutState, ResState, SystemParam}, }; use bevy_utils::tracing::trace; use std::{ @@ -51,7 +50,7 @@ struct EventInstance { } #[derive(Debug)] -enum State { +enum BufferState { A, B, } @@ -125,7 +124,7 @@ pub struct Events { a_start_event_count: usize, b_start_event_count: usize, event_count: usize, - state: State, + state: BufferState, } impl Default for Events { @@ -136,69 +135,134 @@ impl Default for Events { event_count: 0, events_a: Vec::new(), events_b: Vec::new(), - state: State::A, + state: BufferState::A, } } } -fn map_instance_event_with_id(event_instance: &EventInstance) -> (&T, EventId) { - (&event_instance.event, event_instance.event_id) -} - -fn map_instance_event(event_instance: &EventInstance) -> &T { - &event_instance.event -} +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) { + let event_id = EventId { + id: self.event_count, + _marker: PhantomData, + }; + trace!("Events::send() -> {}", event_id); -/// Reads events of type `T` in order and tracks which events have already been read. -#[derive(SystemParam)] -pub struct EventReader<'a, T: Component> { - last_event_count: Local<'a, (usize, PhantomData)>, - events: Res<'a, Events>, -} + let event_instance = EventInstance { event_id, event }; -/// Sends events of type `T`. -#[derive(SystemParam)] -pub struct EventWriter<'a, T: Component> { - events: ResMut<'a, Events>, -} + match self.state { + BufferState::A => self.events_a.push(event_instance), + BufferState::B => self.events_b.push(event_instance), + } -impl<'a, T: Component> EventWriter<'a, T> { - pub fn send(&mut self, event: T) { - self.events.send(event); + self.event_count += 1; } - pub fn send_batch(&mut self, events: impl Iterator) { - self.events.extend(events); + /// Gets a new [ManualEventReader]. This will include all events already in the event buffers. + pub fn get_reader(&self) -> ManualEventReader { + ManualEventReader { + last_event_count: 0, + _marker: PhantomData, + } } -} - -pub struct ManualEventReader { - last_event_count: usize, - _marker: PhantomData, -} -impl Default for ManualEventReader { - fn default() -> Self { + /// Gets a new [ManualEventReader]. This will ignore all events already in the event buffers. It + /// will read all future events. + pub fn get_reader_current(&self) -> ManualEventReader { ManualEventReader { - last_event_count: 0, - _marker: Default::default(), + last_event_count: self.event_count, + _marker: PhantomData, } } -} -impl ManualEventReader { - /// See [`EventReader::iter`] - pub fn iter<'a>(&mut self, events: &'a Events) -> impl DoubleEndedIterator { - internal_event_reader(&mut self.last_event_count, events).map(|(e, _)| e) + /// Swaps the event buffers and clears the oldest event buffer. In general, this should be + /// called once per frame/update. + pub fn update(&mut self) { + match self.state { + BufferState::A => { + self.events_b = Vec::new(); + self.state = BufferState::B; + self.b_start_event_count = self.event_count; + } + BufferState::B => { + self.events_a = Vec::new(); + self.state = BufferState::A; + self.a_start_event_count = self.event_count; + } + } } - /// See [`EventReader::iter_with_id`] - pub fn iter_with_id<'a>( - &mut self, - events: &'a Events, - ) -> impl DoubleEndedIterator)> { - internal_event_reader(&mut self.last_event_count, events) + /// A system that calls [Events::update] once per frame on all events of the specified type. + /// + /// This clears all events from the previous frame, + /// and advances the current frame's buffer so it is ready to be cleared. + pub fn update_system( + mut resource_events: ResMut, + mut component_events: Query<&mut Self>, + ) { + resource_events.update(); + component_events.for_each_mut(|mut e| e.update()); + } + + /// Removes all events. + pub fn clear(&mut self) { + self.events_a.clear(); + self.events_b.clear(); + } + + /// Creates a draining iterator that removes all events. + pub fn drain(&mut self) -> impl DoubleEndedIterator + '_ { + self.drain_with_id().map(|(e, _)| e) + } + + /// Creates a draining iterator that returns both events and their ids + pub fn drain_with_id(&mut self) -> impl DoubleEndedIterator)> + '_ { + let event_instances = match self.state { + BufferState::A => self.events_b.drain(..).chain(self.events_a.drain(..)), + BufferState::B => self.events_a.drain(..).chain(self.events_b.drain(..)), + }; + + event_instances.map(|ei| { + trace!("Events::drain_with_id -> {}", ei.event_id); + (ei.event, ei.event_id) + }) + } + + pub fn extend(&mut self, events: I) + where + I: Iterator, + { + for event in events { + self.send(event); + } } + + /// Iterates over events that happened since the last "update" call. + /// WARNING: You probably don't want to use this call. In most cases you should use an + /// `EventReader`. You should only use this if you know you only need to consume events + /// between the last `update()` call and your call to `iter_current_update_events`. + /// If events happen outside that window, they will not be handled. For example, any events that + /// happen after this call and before the next `update()` call will be dropped. + pub fn iter_current_update_events(&self) -> impl DoubleEndedIterator { + match self.state { + BufferState::A => self.events_a.iter().map(map_instance_event), + BufferState::B => self.events_b.iter().map(map_instance_event), + } + } +} + +// Needed to ensure match branches have same type signature +// As closures never have the same type +fn map_instance_event_with_id(event_instance: &EventInstance) -> (&T, EventId) { + (&event_instance.event, event_instance.event_id) +} + +// Needed to ensure match branches have same type signature +// As closures never have the same type +fn map_instance_event(event_instance: &EventInstance) -> &T { + &event_instance.event } /// Like [`iter_with_id`](EventReader::iter_with_id) except not emitting any traces for read @@ -221,7 +285,7 @@ fn internal_event_reader<'a, T>( }; *last_event_count = events.event_count; match events.state { - State::A => events + BufferState::A => events .events_b .get(b_index..) .unwrap_or_else(|| &[]) @@ -235,7 +299,7 @@ fn internal_event_reader<'a, T>( .iter() .map(map_instance_event_with_id), ), - State::B => events + BufferState::B => events .events_a .get(a_index..) .unwrap_or_else(|| &[]) @@ -251,6 +315,42 @@ fn internal_event_reader<'a, T>( ), } } +/// Sends events of type `T`. +#[derive(SystemParam)] +pub struct EventWriter<'a, T: Component> { + events: &'a mut Events, +} + +impl<'a, T: Component> EventWriter<'a, T> { + pub fn new(events: &'a mut Events) -> Self { + EventWriter::<'a, T> { events } + } + + pub fn send(&mut self, event: T) { + self.events.send(event); + } + + pub fn send_batch(&mut self, events: impl Iterator) { + self.events.extend(events); + } +} + +/// Reads events of type `T` in order and tracks which events have already been read. +#[derive(SystemParam)] +pub struct EventReader<'a, T: Component> { + last_event_count: EventCount, + events: &'a Events, +} + +/// Wrapper struct used by [EventReader] to count events +pub struct EventCount(usize, PhantomData); + +// #[derive(Default)] doesn't handle PhantomData properly, so we're stuck with a manual impl +impl Default for EventCount { + fn default() -> Self { + EventCount(0, PhantomData::default()) + } +} impl<'a, T: Component> EventReader<'a, T> { /// Iterates over the events this EventReader has not seen yet. This updates the EventReader's @@ -269,120 +369,100 @@ impl<'a, T: Component> EventReader<'a, T> { } } -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) { - let event_id = EventId { - id: self.event_count, - _marker: PhantomData, - }; - trace!("Events::send() -> {}", event_id); - - let event_instance = EventInstance { event_id, event }; - - match self.state { - State::A => self.events_a.push(event_instance), - State::B => self.events_b.push(event_instance), - } - - self.event_count += 1; - } - - /// Gets a new [ManualEventReader]. This will include all events already in the event buffers. - pub fn get_reader(&self) -> ManualEventReader { - ManualEventReader { - last_event_count: 0, - _marker: PhantomData, - } - } - - /// Gets a new [ManualEventReader]. This will ignore all events already in the event buffers. It - /// will read all future events. - pub fn get_reader_current(&self) -> ManualEventReader { - ManualEventReader { - last_event_count: self.event_count, - _marker: PhantomData, - } - } +/// Reads and consumes all events of type T +/// +/// Useful for manual event cleanup when [AppBuilder::add_event::] is omitted, +/// allowing events to accumulate on your components or resources until consumed. +/// Note: due to the draining nature of this reader, you probably only want one +/// EventConsumer per event storage location + event type combination. +#[derive(SystemParam)] +pub struct EventConsumer<'a, T: Component> { + events: &'a mut Events, +} - /// Swaps the event buffers and clears the oldest event buffer. In general, this should be - /// called once per frame/update. - pub fn update(&mut self) { - match self.state { - State::A => { - self.events_b = Vec::new(); - self.state = State::B; - self.b_start_event_count = self.event_count; - } - State::B => { - self.events_a = Vec::new(); - self.state = State::A; - self.a_start_event_count = self.event_count; - } - } +impl<'a, T: Component> EventConsumer<'a, T> { + /// Drains all available events this EventConsumer has access to into an iterator + pub fn drain(self) -> impl DoubleEndedIterator + 'a { + self.events.drain() } - /// A system that calls [Events::update] once per frame. - pub fn update_system(mut events: ResMut) { - events.update(); + /// Drains all available events this EventConsumer has access to into an iterator and returns the id + pub fn drain_with_id(self) -> impl DoubleEndedIterator)> + 'a { + self.events.drain_with_id() } +} - /// Removes all events. - pub fn clear(&mut self) { - self.events_a.clear(); - self.events_b.clear(); - } +pub struct ManualEventReader { + last_event_count: usize, + _marker: PhantomData, +} - /// Creates a draining iterator that removes all events. - pub fn drain(&mut self) -> impl Iterator + '_ { - let map = |i: EventInstance| i.event; - match self.state { - State::A => self - .events_b - .drain(..) - .map(map) - .chain(self.events_a.drain(..).map(map)), - State::B => self - .events_a - .drain(..) - .map(map) - .chain(self.events_b.drain(..).map(map)), +impl Default for ManualEventReader { + fn default() -> Self { + ManualEventReader { + last_event_count: 0, + _marker: Default::default(), } } +} - pub fn extend(&mut self, events: I) - where - I: Iterator, - { - for event in events { - self.send(event); - } +impl ManualEventReader { + /// See [`EventReader::iter`] + pub fn iter<'a>(&mut self, events: &'a Events) -> impl DoubleEndedIterator { + internal_event_reader(&mut self.last_event_count, events).map(|(e, _)| e) } - /// Iterates over events that happened since the last "update" call. - /// WARNING: You probably don't want to use this call. In most cases you should use an - /// `EventReader`. You should only use this if you know you only need to consume events - /// between the last `update()` call and your call to `iter_current_update_events`. - /// If events happen outside that window, they will not be handled. For example, any events that - /// happen after this call and before the next `update()` call will be dropped. - pub fn iter_current_update_events(&self) -> impl DoubleEndedIterator { - match self.state { - State::A => self.events_a.iter().map(map_instance_event), - State::B => self.events_b.iter().map(map_instance_event), - } + /// See [`EventReader::iter_with_id`] + pub fn iter_with_id<'a>( + &mut self, + events: &'a Events, + ) -> impl DoubleEndedIterator)> { + internal_event_reader(&mut self.last_event_count, events) } } #[cfg(test)] mod tests { use super::*; + use crate::schedule::{Stage, SystemStage}; + use crate::system::IntoSystem; + use crate::world::World; #[derive(Copy, Clone, PartialEq, Eq, Debug)] struct TestEvent { i: usize, } + #[test] + fn event_system_params() { + let world = World::default(); + struct E; + fn writes(ew: EventWriter) { + ew.send(E) + } + fn reads(er: EventReader) { + er.iter(); + } + fn consumes(ec: EventConsumer) { + ec.drain(); + } + + let mut stage1 = SystemStage::parallel(); + stage1.add_system(writes.system()); + stage1.add_system(reads.system()); + + stage1.run(&mut World::default()); + let current_events = world.get_resource::>().unwrap(); + assert!(current_events.events_a.len() == 1); + + let mut stage2 = SystemStage::parallel(); + stage2.add_system(consumes.system()); + + stage2.run(&mut World::default()); + let current_events = world.get_resource::>().unwrap(); + assert!(current_events.events_a.len() == 0); + } + #[test] fn test_events() { let mut events = Events::::default(); diff --git a/crates/bevy_ecs/src/query/event.rs b/crates/bevy_ecs/src/query/event.rs new file mode 100644 index 0000000000000..32ec7c110e0a5 --- /dev/null +++ b/crates/bevy_ecs/src/query/event.rs @@ -0,0 +1,137 @@ +use super::{ + super::event::{EventWriter, Events}, + Access, Fetch, FetchState, FilteredAccess, WorldQuery, WriteFetch, WriteState, +}; +use crate::{ + archetype::{Archetype, ArchetypeComponentId}, + component::{Component, ComponentId, StorageType}, + prelude::World, + storage::{Table, Tables}, +}; +use std::any::TypeId; + +impl<'a, T: Component> WorldQuery for EventWriter<'a, T> { + type Fetch = EventWriterFetch; + type State = EventWriterState; +} + +pub struct EventWriterFetch { + /// EventWriter query parameters require write access to &mut Events + write_fetch: WriteFetch>, + storage_type: StorageType, +} + +impl<'a, T: Component> Fetch<'a> for EventWriterFetch { + /// EventWriter queries return an EventWriter in each item + type Item = EventWriter<'a, T>; + /// This is the corresponding S: FetchState type + type State = EventWriterState; + + /// Checks the storage type of the corresponding Events component + fn is_dense(&self) -> bool { + match self.storage_type { + StorageType::SparseSet => false, + StorageType::Table => true, + } + } + + unsafe fn init( + world: &World, + state: &Self::State, + last_change_tick: u32, + change_tick: u32, + ) -> Self { + EventWriterFetch { + write_fetch: WriteFetch::>::init( + world, + &state.write_state, + last_change_tick, + change_tick, + ), + storage_type: world + .components + .get_info(world.components.get_id(TypeId::of::>()).unwrap()) + .unwrap() + .storage_type(), + } + } + + unsafe fn set_archetype( + &mut self, + state: &Self::State, + archetype: &Archetype, + tables: &Tables, + ) { + self.write_fetch + .set_archetype(&state.write_state, archetype, tables); + } + + unsafe fn set_table(&mut self, state: &Self::State, table: &Table) { + self.write_fetch.set_table(&state.write_state, table); + } + + /// Returns the EventWriter of the next entity when the storage type of the query is sparse + unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item { + let events = self + .write_fetch + .archetype_fetch(archetype_index) + .into_inner(); + EventWriter::new(events) + } + + /// Returns the EventWriter of the next entity when the storage type of the query is dense + unsafe fn table_fetch(&mut self, table_row: usize) -> Self::Item { + let events = self.write_fetch.archetype_fetch(table_row).into_inner(); + EventWriter::new(events) + } +} +pub struct EventWriterState { + event_component_id: ComponentId, + /// EventWriter query parameters require write access to &mut Events + write_state: WriteState>, +} + +unsafe impl FetchState for EventWriterState { + fn init(world: &mut World) -> Self { + let event_component_id = world.components.get_id(TypeId::of::>()).unwrap(); + EventWriterState { + event_component_id, + write_state: WriteState::>::init(world), + } + } + + /// Access is based on &Events + fn update_component_access(&self, access: &mut FilteredAccess) { + if access.access().has_write(self.event_component_id) { + panic!("&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", + std::any::type_name::>()); + } + access.add_read(self.event_component_id) + } + + /// Access is based on &Events + fn update_archetype_component_access( + &self, + archetype: &Archetype, + access: &mut Access, + ) { + if let Some(archetype_component_id) = + archetype.get_archetype_component_id(self.event_component_id) + { + access.add_read(archetype_component_id); + } + } + + /// Matches based on &Events + fn matches_archetype(&self, archetype: &Archetype) -> bool { + archetype.contains(self.event_component_id) + } + + /// Matches based on &Events + fn matches_table(&self, table: &Table) -> bool { + table.has_column(self.event_component_id) + } +} + +// TODO: add tests +mod tests {} diff --git a/crates/bevy_ecs/src/query/mod.rs b/crates/bevy_ecs/src/query/mod.rs index 95b4d7b6e8912..27ea0181c61ec 100644 --- a/crates/bevy_ecs/src/query/mod.rs +++ b/crates/bevy_ecs/src/query/mod.rs @@ -1,4 +1,5 @@ mod access; +mod event; mod fetch; mod filter; mod iter; @@ -9,6 +10,7 @@ pub use fetch::*; pub use filter::*; pub use iter::*; pub use state::*; +pub use state::*; #[cfg(test)] mod tests { diff --git a/examples/README.md b/examples/README.md index 096afc63e4193..42246f7c8d5a6 100644 --- a/examples/README.md +++ b/examples/README.md @@ -154,6 +154,7 @@ Example | File | Description `fixed_timestep` | [`ecs/fixed_timestep.rs`](./ecs/fixed_timestep.rs) | Shows how to create systems that run every fixed timestep, rather than every tick `hierarchy` | [`ecs/hierarchy.rs`](./ecs/hierarchy.rs) | Creates a hierarchy of parents and children entities `parallel_query` | [`ecs/parallel_query.rs`](./ecs/parallel_query.rs) | Illustrates parallel queries with `ParallelIterator` +`per_entity_events` | [`ecs/per_entity_events.rs`](./ecs/per_entity_events.rs) | Demonstrates how to store events on individual entities in a channel-like fashion. `query_bundle` | [`ecs/query_bundle.rs`](./ecs/query_bundle.rs) | Shows how to query entities that contain components in a `Bundle` `removal_detection` | [`ecs/removal_detection.rs`](./ecs/removal_detection.rs) | Query for entities that had a specific component removed in a previous stage during the current frame. `startup_system` | [`ecs/startup_system.rs`](./ecs/startup_system.rs) | Demonstrates a startup system (one that runs once when the app starts up) diff --git a/examples/ecs/per_entity_events.rs b/examples/ecs/per_entity_events.rs new file mode 100644 index 0000000000000..ddded15c46c45 --- /dev/null +++ b/examples/ecs/per_entity_events.rs @@ -0,0 +1,332 @@ +use bevy::prelude::*; +use bevy::{ + app::{Events, ManualEventReader}, + core::FixedTimestep, +}; + +/// In this example, we show how to store events of a given type +/// as a component on individual entities rather than in a single resource. +/// +/// This pattern allows you to dispatch events directly to the entity that needs to handle them, +/// letting you avoid storing the `Entity` in the event, and prevents your from needing to either +/// repeatedly scan the entire event list for relevant events or look-up the appropriate entity using +/// slow query.get(my_entity) calls that have poor cache-locality. +/// +/// By storing the events on particular entities, +/// you can treat each entity as a seperate event-channel, +/// letting you create new events intended for only certain consumers +/// without forcing you to create a new event type to disambiguate. +/// +/// This specific example shows a simple input -> action dispatch use case, +/// where this pattern helps to avoid messy rechecking and allows simple merging of multiple event input streams. +fn main() { + App::build() + .add_plugins(DefaultPlugins) + // Adding events using .add_event:: will cause all resources and components of type T + // to be automatically cleaned in a double-buffer fashion by inserting an appropriate system + // + // You can avoid this behavior and manually clean up your events by simply adding events + // as vanilla components or resources + .add_event::() + .add_event::() + .add_startup_system(setup.system()) + .add_system(select_entity.system()) + .add_system( + input_dispatch + .system() + .label("input_dispatch") + .before("action_handling"), + ) + .add_system(cycle_color.system().label("action_handling")) + .add_system(add_number.system().label("action_handling")) + .add_system(scale_selected.system().after("action_handling")) + .add_system(update_text_color.system().after("action_handling")) + .add_system( + move_text + .system() + .label("action_handling") + .with_run_criteria(FixedTimestep::step(TIME_STEP)), + ) + .run() +} + +pub mod setup { + #[derive(Bundle)] + struct InteractableBundle { + #[bundle] + text_bundle: TextBundle, + selectable: Selectable, + rainbow: ColorChoices, + cycle_color_events: Events, + move_events: Events, + add_number_events: Events, + } + + impl InteractableBundle { + // FIXME: fix position + fn new(x: f32, y: f32, font_handle: &Handle) -> Self { + InteractableBundle { + text_bundle: TextBundle { + text: Text::with_section( + "0", + TextStyle { + font: font_handle.clone(), + font_size: 60.0, + color: Color::WHITE, + }, + TextAlignment { + vertical: VerticalAlign::Center, + horizontal: HorizontalAlign::Center, + }, + ), + transform: Transform::from_xyz(x, y, 0.0), + ..Default::default() + }, + selectable: Selectable, + rainbow: ColorChoices::Red, + cycle_color_events: Events::::default(), + move_events: Events::default(), + add_number_events: Events::::default(), + } + } + } + + fn setup(mut commands: Commands, asset_server: Res) { + commands.spawn_bundle(OrthographicCameraBundle::new_2d()); + + let font_handle = asset_server.load("fonts/FiraSans-Bold.ttf"); + // Spawns the first entity, and grabs the Entity id that is being allocated + let first_entity = commands + .spawn_bundle(InteractableBundle::new(-200.0, 0.0, &font_handle)) + .id(); + commands.insert_resource(Selected { + entity: first_entity, + }); + + commands.spawn_bundle(InteractableBundle::new(0.0, 0.0, &font_handle)); + commands.spawn_bundle(InteractableBundle::new(200.0, 0.0, &font_handle)); + } + +} + +pub mod selection { +// Tracks which entity is selected +struct Selected { + entity: Entity, +} +// Marks entities as selectable +struct Selectable; + +enum CycleBehavior { + Forward, + Back, +} + +/// Cycles through entities appropriately based on input +fn select_entity( + mut query: Query>, + mut selected: ResMut, + keyboard_input: Res>, +) { + let cycle_behavior: CycleBehavior = if keyboard_input.just_pressed(KeyCode::Tab) { + if keyboard_input.pressed(KeyCode::LShift) || keyboard_input.pressed(KeyCode::RShift) { + CycleBehavior::Back + } else { + CycleBehavior::Forward + } + } else { + return; + }; + + let mut entities = Vec::::new(); + // FIXME: Move to `.for_each` when https://github.com/bevyengine/bevy/issues/753 is resolved + query.for_each_mut(|entity| entities.push(entity.clone())); + + let current_position = entities.iter().position(|&e| e == selected.entity).unwrap() as isize; + + let new_position = match cycle_behavior { + // We have to convert to isize for this step to avoid underflows when current_postion == 0 + CycleBehavior::Forward => (current_position + 1).rem_euclid(entities.len() as isize), + CycleBehavior::Back => (current_position - 1).rem_euclid(entities.len() as isize), + }; + + selected.entity = entities[new_position as usize]; +} + +fn scale_selected( + mut query: Query<(Entity, &mut Text), With>, + selected: Res, +) { + // Only do work when the selection is changed + if !selected.is_changed() { + return; + } + + for (entity, mut text) in query.iter_mut() { + if entity == selected.entity { + text.sections[0].style.font_size = 90.0; + } else { + text.sections[0].style.font_size = 60.0; + } + } +} + +} + +pub mod input { +/// Dispatches actions to entities based on the input +/// Note that we can store several events at once! +/// Try pressing both "1" and "3" to add 4 to the selected display +fn input_dispatch( + // You could also access the &Events component directly + // then send events to that component with `Events::send` + mut query: Query< + ( + EventWriter, + EventWriter, + EventWriter, + ), + With, + >, + selected: Res, + keyboard_input: ResMut>, +) { + use KeyCode::*; + + let (mut cycle_actions, mut add_actions) = query.get_mut(selected.entity).unwrap(); + + for key_code in keyboard_input.get_just_pressed() { + match key_code { + // Color changing + Space => cycle_actions.send(CycleColorAction), + // Movement + Left => move_actions.send(MoveAction { + transform: Transform::from_xyz(-MOVE_DISTANCE, 0.0, 0.0), + }), + Right => move_actions.send(MoveAction { + transform: Transform::from_xyz(MOVE_DISTANCE, 0.0, 0.0), + }), + Down => move_actions.send(MoveAction { + transform: Transform::from_xyz(0.0, -MOVE_DISTANCE, 0.0), + }), + Up => move_actions.send(MoveAction { + transform: Transform::from_xyz(0.0, MOVE_DISTANCE, 0.0), + }), + _ => (), + } + + // Inputs for sending numbers to be added + if (key_code as u8) < 10 { + add_actions.send(AddNumberAction { + // The keycode for KeyCode::Key1 is 0 + number: key_code as u8 + 1, + }); + } + } +} + +} + +pub mod colors { + struct CycleColorAction; + enum ColorChoices { + Red, + Blue, + Violet, + } + + impl Iterator for ColorChoices { + type Item = Self; + + fn next(&mut self) -> Option { + use ColorChoices::*; + Some(match *self { + Red => Blue, + Blue => Violet, + Violet => Red, + }) + } + } + + impl From<&ColorChoices> for Color { + fn from(rainbow: &ColorChoices) -> Color { + use ColorChoices::*; + match rainbow { + Red => Color::RED, + Blue => Color::BLUE, + Violet => Color::VIOLET, + } + } + } + + fn cycle_color(mut query: Query<(&mut ColorChoices, EventReader)>) { + for (mut color, action_queue) in query.iter_mut() { + for _ in action_queue.iter() { + *color = color.next().unwrap(); + } + } + } + + fn update_text_color(mut query: Query<(&mut Text, &ColorChoices), Changed>) { + for (mut text, rainbow) in query.iter_mut() { + text.sections[0].style.color = rainbow.into(); + } + } + +} + +pub mod movement { + struct MoveAction { + transform: Transform, + } + + const MOVE_DISTANCE: f32 = 100.0; + const TIME_STEP: f32 = 2; + + // When events are registered in the AppBuilder using .add_event(), + // a system will automatically be created to clean them up after two frames. + // This can be problematic if your event-consuming systems ever skip frames (such as due to a fixed timestep run criteria). + // We can get around this by not registering them, and instead handling clean-up by consuming them when read. + // Be careful though: once consumed, other systems will not be able to read them! + fn move_text(query: Query<(&mut Transform, EventConsumer)>) { + for (mut transform, events) in query.iter() { + // Unlike EventReaders which simply iterate, EventConsumers drain the events they read + for move_action in events.drain() { + *transform += move_action.transform * TIME_STEP; + } + } + } + +} + +pub mod addition { +// Or store data to be responded to +struct AddNumberAction { + number: u8, +} +// Just as when using Events as a resource, you can work with `Events` directly instead +// EventReader and EventWriter are just convenient wrappers that better communicate intent +// And store state automatically for you +fn add_number( + mut query: Query<(&mut Text, &Events)>, + mut reader: Local>, + selected: Res, +) { + let (mut text, action_queue) = query.get_mut(selected.entity).unwrap(); + // Because we only care about one entity at a time, we can store the event reader manually + // in a Local resource as part of the system's data + // This logic is handled for you, storing one EventReader per entity when you query for an EventReader + if selected.is_changed() { + // If the resource selected is changed, we need to rebuild a new event reader + *reader = action_queue.get_reader(); + } + + for action in reader.iter(&action_queue) { + let current_number: u8 = text.sections[0].value.clone().parse().unwrap(); + // Wrap addition, rather than overflowing + let new_number = ((current_number + action.number) as u16) % std::u8::MAX as u16; + text.sections[0].value = new_number.to_string(); + } +} + +} \ No newline at end of file