Skip to main content

ui_target_camera/
ui_target_camera.rs

1//! Demonstrates how to use `UiTargetCamera` and camera ordering.
2
3use bevy::color::palettes::css::BLUE;
4use bevy::color::palettes::css::GREEN;
5use bevy::color::palettes::css::RED;
6use bevy::color::palettes::css::YELLOW;
7use bevy::prelude::*;
8
9fn main() {
10    App::new()
11        .add_plugins(DefaultPlugins)
12        .add_systems(Startup, setup)
13        .run();
14}
15
16const BOX_SIZE: f32 = 100.;
17
18fn setup(mut commands: Commands) {
19    // Root UI node displaying instructions.
20    // Has no `UiTargetCamera`; the highest-order camera rendering to the primary window will be chosen automatically.
21    commands.spawn((
22        Node {
23                align_self: AlignSelf::Center,
24                justify_self: JustifySelf::Center,
25                justify_content: JustifyContent::Center,
26                bottom: px(2. * BOX_SIZE),
27                ..default()
28            },
29            Text::new("Each box is rendered by a different camera\n* left-click: increase the camera's order\n* right-click: decrease the camera's order")
30        ));
31
32    for (i, color) in [RED, GREEN, BLUE].into_iter().enumerate() {
33        let camera_entity = commands
34            .spawn((
35                // Ordering behavior is the same using `Camera3d`.
36                Camera2d,
37                Camera {
38                    // The viewport will be cleared according to the `ClearColorConfig` of the camera with the lowest order, skipping cameras set to `ClearColorConfig::NONE`.
39                    // If all are set to `ClearColorConfig::NONE`, no clear color is used.
40                    clear_color: ClearColorConfig::Custom(color.into()),
41                    order: i as isize,
42                    ..Default::default()
43                },
44            ))
45            .id();
46
47        // Label each box with the order of its camera target
48        let label_entity = commands
49            .spawn((
50                Text(format!("{i}")),
51                TextFont::from_font_size(50.),
52                TextColor(color.into()),
53            ))
54            .id();
55
56        commands
57            .spawn((
58                Node {
59                    align_self: AlignSelf::Center,
60                    justify_self: JustifySelf::Center,
61                    justify_content: JustifyContent::Center,
62                    align_items: AlignItems::Center,
63                    left: px(0.67 * BOX_SIZE * (i as f32 - 1.)),
64                    top: px(0.67 * BOX_SIZE * (i as f32 - 1.)),
65                    width: px(BOX_SIZE),
66                    height: px(BOX_SIZE),
67                    border: px(0.1 * BOX_SIZE).into(),
68                    ..default()
69                },
70                // Bevy UI doesn't support `RenderLayers`. Each UI layout can only have one render target, selected using `UiTargetCamera`.
71                UiTargetCamera(camera_entity),
72                BackgroundColor(Color::BLACK),
73                BorderColor::all(YELLOW),
74            ))
75            .observe(
76                move |on_pressed: On<Pointer<Press>>,
77                      mut label_query: Query<&mut Text>,
78                      mut camera_query: Query<&mut Camera>| {
79                    let Ok(mut label_text) = label_query.get_mut(label_entity) else {
80                        return;
81                    };
82                    let Ok(mut camera) = camera_query.get_mut(camera_entity) else {
83                        return;
84                    };
85                    camera.order += match on_pressed.button {
86                        PointerButton::Primary => 1,
87                        _ => -1,
88                    };
89                    label_text.0 = format!("{}", camera.order);
90                },
91            )
92            .add_child(label_entity);
93    }
94}