docs/bevy/examples/ecs/extraction.rs 4.6 K · 130 lines · raw · history

1 //! Demonstrates different ways of extracting components from the main world to the render world.
2 //!
3 //! This is usually done as an intermediary step for transferring data to the GPU, making
4 //! it accessible inside shaders.
5 //!
6 //! In this example, multiple entities are spawned, each with a different marker component: A, B, C.
7 //! Each component contains the current elapsed time, updated each frame on the Main World, and is
8 //! extracted to the render world in a different way.
9
10 use bevy::prelude::*;
11 use bevy::render::{
12 extract_component::{ExtractComponent, ExtractComponentPlugin},
13 sync_world::{RenderEntity, SyncToRenderWorld},
14 Extract, Render, RenderApp,
15 };
16
17 // The A component is extracted automatically through `ExtractComponentPlugin`. For this,
18 // it is required to implement `ExtractComponent`. You can do a custom implementation if you wish to
19 // do a custom extraction instead of just cloning the entire component.
20 //
21 // To be noted that the `SyncToRenderWorld` component, which spawns the corresponding entity on the Render World,
22 // is automatically added as a requirement through the `ExtractComponentPlugin`.
23 #[derive(Component, Clone, ExtractComponent, Debug)]
24 #[extract_app(RenderApp)]
25 struct A(pub f32);
26
27 // The B component is extracted manually inside the `extract_components` system.
28 // `SyncToRenderWorld` ensures that an equivalent entity will be spawned in the Render World
29 // and the two will be associated in the Extract schedule through the `RenderEntity` component.
30 #[derive(Component, Clone, Debug)]
31 #[require(SyncToRenderWorld)]
32 struct B(pub f32);
33
34 // The C component is the same B, but it only extracts when the `Space` key is pressed.
35 #[derive(Component, Clone, Debug)]
36 #[require(SyncToRenderWorld)]
37 struct C(pub f32);
38
39 // Message sent when the `Space` key is pressed, causing the extraction of C.
40 #[derive(Message)]
41 struct ExtractMessage;
42
43 // Resource inserted in each World, used to display its name.
44 #[derive(Resource)]
45 struct WorldName(pub String);
46
47 fn main() {
48 let mut app = App::new();
49
50 // Main World
51 app.insert_resource(WorldName("Main World".into()))
52 .add_plugins((
53 DefaultPlugins,
54 // Plugin for automatically extracting A.
55 ExtractComponentPlugin::<A>::default(),
56 ))
57 .add_message::<ExtractMessage>()
58 .add_systems(Startup, setup)
59 .add_systems(Update, (set_time, trigger_extraction, display_state));
60
61 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
62 return;
63 };
64
65 // Render World
66 render_app
67 .insert_resource(WorldName("Render World".into()))
68 .add_systems(ExtractSchedule, extract_components)
69 .add_systems(Render, display_state);
70
71 app.run();
72 }
73
74 // Spawns the components on the Main World. Runs on Startup.
75 fn setup(mut commands: Commands, time: Res<Time>) {
76 commands.spawn(A(time.elapsed_secs()));
77 commands.spawn(B(time.elapsed_secs()));
78 commands.spawn(C(time.elapsed_secs()));
79 }
80
81 // Sets the elapsed time on each of the components on the Main World. Runs each frame.
82 fn set_time(mut a: Single<&mut A>, mut b: Single<&mut B>, mut c: Single<&mut C>, time: Res<Time>) {
83 a.0 = time.elapsed_secs();
84 b.0 = time.elapsed_secs();
85 c.0 = time.elapsed_secs();
86 }
87
88 // Displays the values from each of the components. The same system is used for both Worlds.
89 fn display_state(
90 a: Option<Single<(Entity, &A)>>,
91 b: Option<Single<(Entity, &B)>>,
92 c: Option<Single<(Entity, &C)>>,
93
94 // Resource used to debug the name of the World.
95 world_name: Res<WorldName>,
96 ) {
97 let (a, b, c) = (
98 a.map(Single::into_inner),
99 b.map(Single::into_inner),
100 c.map(Single::into_inner),
101 );
102 info!(?a, ?b, ?c, "{: >12}", world_name.0);
103 }
104
105 // Writes a message when the `Space` key is pressed, which is later read by the `extract_components` system.
106 fn trigger_extraction(mut writer: MessageWriter<ExtractMessage>, keys: Res<ButtonInput<KeyCode>>) {
107 if keys.pressed(KeyCode::Space) {
108 writer.write(ExtractMessage);
109 }
110 }
111
112 // System that Extracts B each frame, and C only when the `Space` key was just pressed (see the `trigger_extraction` system).
113 // Extraction is done by inserting a clone of the component on the corresponding Render World entity.
114 fn extract_components(
115 b: Extract<Query<(RenderEntity, &B)>>,
116 c: Extract<Query<(RenderEntity, &C)>>,
117 mut reader: Extract<MessageReader<ExtractMessage>>,
118 mut commands: Commands,
119 ) {
120 for (entity, b) in &b {
121 commands.entity(entity).insert(b.clone());
122 }
123
124 if !reader.is_empty() {
125 for (entity, c) in &c {
126 commands.entity(entity).insert(c.clone());
127 }
128 reader.clear();
129 }
130 }