Skip to content

Commit feb3d37

Browse files
Cleaned up example by organizing code into modules
1 parent 88052f3 commit feb3d37

File tree

1 file changed

+137
-121
lines changed

1 file changed

+137
-121
lines changed

examples/ecs/per_entity_events.rs

Lines changed: 137 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ use bevy::{
1919
///
2020
/// This specific example shows a simple input -> action dispatch use case,
2121
/// where this pattern helps to avoid messy rechecking and allows simple merging of multiple event input streams.
22-
///
2322
fn main() {
2423
App::build()
2524
.add_plugins(DefaultPlugins)
@@ -45,110 +44,78 @@ fn main() {
4544
.add_system(
4645
move_text
4746
.system()
47+
.label("action_handling")
4848
.with_run_criteria(FixedTimestep::step(TIME_STEP)),
4949
)
5050
.run()
5151
}
5252

53-
// Tracks which entity is selected
54-
struct Selected {
55-
entity: Entity,
56-
}
57-
// Marks entities as selectable
58-
struct Selectable;
59-
#[derive(Bundle)]
60-
struct InteractableBundle {
61-
#[bundle]
62-
text_bundle: TextBundle,
63-
selectable: Selectable,
64-
rainbow: ColorChoices,
65-
cycle_color_events: Events<CycleColorAction>,
66-
move_events: Events<MoveAction>,
67-
add_number_events: Events<AddNumberAction>,
68-
}
69-
70-
impl InteractableBundle {
71-
// FIXME: fix position
72-
fn new(x: f32, y: f32, font_handle: &Handle<Font>) -> Self {
73-
InteractableBundle {
74-
text_bundle: TextBundle {
75-
text: Text::with_section(
76-
"0",
77-
TextStyle {
78-
font: font_handle.clone(),
79-
font_size: 60.0,
80-
color: Color::WHITE,
81-
},
82-
TextAlignment {
83-
vertical: VerticalAlign::Center,
84-
horizontal: HorizontalAlign::Center,
85-
},
86-
),
87-
transform: Transform::from_xyz(x, y, 0.0),
88-
..Default::default()
89-
},
90-
selectable: Selectable,
91-
rainbow: ColorChoices::Red,
92-
cycle_color_events: Events::<CycleColorAction>::default(),
93-
move_events: Events<MoveAction>::default(),
94-
add_number_events: Events::<AddNumberAction>::default(),
95-
}
53+
pub mod setup {
54+
#[derive(Bundle)]
55+
struct InteractableBundle {
56+
#[bundle]
57+
text_bundle: TextBundle,
58+
selectable: Selectable,
59+
rainbow: ColorChoices,
60+
cycle_color_events: Events<CycleColorAction>,
61+
move_events: Events<MoveAction>,
62+
add_number_events: Events<AddNumberAction>,
9663
}
97-
}
98-
99-
enum ColorChoices {
100-
Red,
101-
Blue,
102-
Violet,
103-
}
104-
105-
impl Iterator for ColorChoices {
106-
type Item = Self;
107-
108-
fn next(&mut self) -> Option<ColorChoices> {
109-
use ColorChoices::*;
110-
Some(match *self {
111-
Red => Blue,
112-
Blue => Violet,
113-
Violet => Red,
114-
})
115-
}
116-
}
117-
118-
impl From<&ColorChoices> for Color {
119-
fn from(rainbow: &ColorChoices) -> Color {
120-
use ColorChoices::*;
121-
match rainbow {
122-
Red => Color::RED,
123-
Blue => Color::BLUE,
124-
Violet => Color::VIOLET,
64+
65+
impl InteractableBundle {
66+
// FIXME: fix position
67+
fn new(x: f32, y: f32, font_handle: &Handle<Font>) -> Self {
68+
InteractableBundle {
69+
text_bundle: TextBundle {
70+
text: Text::with_section(
71+
"0",
72+
TextStyle {
73+
font: font_handle.clone(),
74+
font_size: 60.0,
75+
color: Color::WHITE,
76+
},
77+
TextAlignment {
78+
vertical: VerticalAlign::Center,
79+
horizontal: HorizontalAlign::Center,
80+
},
81+
),
82+
transform: Transform::from_xyz(x, y, 0.0),
83+
..Default::default()
84+
},
85+
selectable: Selectable,
86+
rainbow: ColorChoices::Red,
87+
cycle_color_events: Events::<CycleColorAction>::default(),
88+
move_events: Events<MoveAction>::default(),
89+
add_number_events: Events::<AddNumberAction>::default(),
90+
}
12591
}
12692
}
93+
94+
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
95+
commands.spawn_bundle(OrthographicCameraBundle::new_2d());
96+
97+
let font_handle = asset_server.load("fonts/FiraSans-Bold.ttf");
98+
// Spawns the first entity, and grabs the Entity id that is being allocated
99+
let first_entity = commands
100+
.spawn_bundle(InteractableBundle::new(-200.0, 0.0, &font_handle))
101+
.id();
102+
commands.insert_resource(Selected {
103+
entity: first_entity,
104+
});
105+
106+
commands.spawn_bundle(InteractableBundle::new(0.0, 0.0, &font_handle));
107+
commands.spawn_bundle(InteractableBundle::new(200.0, 0.0, &font_handle));
108+
}
109+
127110
}
128111

129-
// Events can be simple unit structs
130-
struct CycleColorAction;
131-
// Or store data to be responded to
132-
struct AddNumberAction {
133-
number: u8,
134-
}
135-
136-
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
137-
// Don't forget to include a camera!
138-
commands.spawn_bundle(UiCameraBundle::default());
139-
140-
let font_handle = asset_server.load("fonts/FiraSans-Bold.ttf");
141-
// Spawns the first entity, and grabs the Entity id that is being allocated
142-
let first_entity = commands
143-
.spawn_bundle(InteractableBundle::new(-200.0, 400.0, &font_handle))
144-
.id();
145-
commands.insert_resource(Selected {
146-
entity: first_entity,
147-
});
148-
149-
commands.spawn_bundle(InteractableBundle::new(0.0, 400.0, &font_handle));
150-
commands.spawn_bundle(InteractableBundle::new(200.0, 400.0, &font_handle));
112+
pub mod selection {
113+
// Tracks which entity is selected
114+
struct Selected {
115+
entity: Entity,
151116
}
117+
// Marks entities as selectable
118+
struct Selectable;
152119

153120
enum CycleBehavior {
154121
Forward,
@@ -204,6 +171,9 @@ fn scale_selected(
204171
}
205172
}
206173

174+
}
175+
176+
pub mod input {
207177
/// Dispatches actions to entities based on the input
208178
/// Note that we can store several events at once!
209179
/// Try pressing both "1" and "3" to add 4 to the selected display
@@ -255,20 +225,85 @@ fn input_dispatch(
255225
}
256226
}
257227

258-
fn cycle_color(mut query: Query<(&mut ColorChoices, EventReader<CycleColorAction>)>) {
259-
for (mut color, action_queue) in query.iter_mut() {
260-
for _ in action_queue.iter() {
261-
*color = color.next().unwrap();
228+
}
229+
230+
pub mod colors {
231+
struct CycleColorAction;
232+
enum ColorChoices {
233+
Red,
234+
Blue,
235+
Violet,
236+
}
237+
238+
impl Iterator for ColorChoices {
239+
type Item = Self;
240+
241+
fn next(&mut self) -> Option<ColorChoices> {
242+
use ColorChoices::*;
243+
Some(match *self {
244+
Red => Blue,
245+
Blue => Violet,
246+
Violet => Red,
247+
})
248+
}
249+
}
250+
251+
impl From<&ColorChoices> for Color {
252+
fn from(rainbow: &ColorChoices) -> Color {
253+
use ColorChoices::*;
254+
match rainbow {
255+
Red => Color::RED,
256+
Blue => Color::BLUE,
257+
Violet => Color::VIOLET,
258+
}
262259
}
263260
}
261+
262+
fn cycle_color(mut query: Query<(&mut ColorChoices, EventReader<CycleColorAction>)>) {
263+
for (mut color, action_queue) in query.iter_mut() {
264+
for _ in action_queue.iter() {
265+
*color = color.next().unwrap();
266+
}
267+
}
268+
}
269+
270+
fn update_text_color(mut query: Query<(&mut Text, &ColorChoices), Changed<ColorChoices>>) {
271+
for (mut text, rainbow) in query.iter_mut() {
272+
text.sections[0].style.color = rainbow.into();
273+
}
274+
}
275+
264276
}
265277

266-
fn update_text_color(mut query: Query<(&mut Text, &ColorChoices), Changed<ColorChoices>>) {
267-
for (mut text, rainbow) in query.iter_mut() {
268-
text.sections[0].style.color = rainbow.into();
278+
pub mod movement {
279+
struct MoveAction {
280+
transform: Transform,
281+
}
282+
283+
const MOVE_DISTANCE: f32 = 100.0;
284+
const TIME_STEP: f32 = 2;
285+
286+
// When events are registered in the AppBuilder using .add_event(),
287+
// a system will automatically be created to clean them up after two frames.
288+
// This can be problematic if your event-consuming systems ever skip frames (such as due to a fixed timestep run criteria).
289+
// We can get around this by not registering them, and instead handling clean-up by consuming them when read.
290+
// Be careful though: once consumed, other systems will not be able to read them!
291+
fn move_text(query: Query<(&mut Transform, EventConsumer<MoveAction>)>) {
292+
for (mut transform, events) in query.iter() {
293+
// Unlike EventReaders which simply iterate, EventConsumers drain the events they read
294+
for move_action in events.drain() {
295+
*transform += move_action.transform * TIME_STEP;
296+
}
297+
}
269298
}
299+
270300
}
271301

302+
pub mod addition {
303+
// Or store data to be responded to
304+
struct AddNumberAction {
305+
number: u8,
306+
}
272307
// Just as when using Events as a resource, you can work with `Events<T>` directly instead
273308
// EventReader and EventWriter are just convenient wrappers that better communicate intent
274309
// And store state automatically for you
@@ -294,23 +329,4 @@ fn add_number(
294329
}
295330
}
296331

297-
const MOVE_DISTANCE: f32 = 100.0;
298-
const TIME_STEP: f32 = 2;
299-
#[derive(Default)]
300-
struct MoveAction {
301-
transform: Transform,
302-
}
303-
304-
// When events are registered in the AppBuilder using .add_event(),
305-
// a system will automatically be created to clean them up after two frames.
306-
// This can be problematic if your event-consuming systems ever skip frames (such as due to a fixed timestep run criteria).
307-
// We can get around this by not registering them, and instead handling clean-up by consuming them when read.
308-
// Be careful though: once consumed, other systems will not be able to read them!
309-
fn move_text(query: Query<(&mut Transform, EventConsumer<MoveAction>)>) {
310-
for (mut transform, events) in query.iter() {
311-
// Unlike EventReaders which simply iterate, EventConsumers drain the events they read
312-
for move_action in events.drain() {
313-
*transform += move_action.transform * TIME_STEP;
314-
}
315-
}
316-
}
332+
}

0 commit comments

Comments
 (0)