Skip to main content

extraction/
extraction.rs

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
10use bevy::prelude::*;
11use 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)]
25struct 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)]
32struct 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)]
37struct C(pub f32);
38
39// Message sent when the `Space` key is pressed, causing the extraction of C.
40#[derive(Message)]
41struct ExtractMessage;
42
43// Resource inserted in each World, used to display its name.
44#[derive(Resource)]
45struct WorldName(pub String);
46
47fn 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.
75fn 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.
82fn 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.
89fn 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.
106fn 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.
114fn 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}