multi_window_text/
multi_window_text.rs

1//! Renders text to multiple windows with different scale factors using both Text and Text2d.
2use bevy::{
3    camera::{visibility::RenderLayers, RenderTarget},
4    color::palettes::css::{LIGHT_CYAN, YELLOW},
5    prelude::*,
6    sprite::Text2dShadow,
7    window::{WindowRef, WindowResolution},
8};
9
10fn main() {
11    App::new()
12        // By default, a primary window is spawned by `WindowPlugin`, contained in `DefaultPlugins`.
13        // The primary window is given the `PrimaryWindow` marker component.
14        .add_plugins(DefaultPlugins.set(WindowPlugin {
15            primary_window: Some(Window {
16                title: "Primary window".to_owned(),
17                // Override the primary window's scale factor and use `1.` (no scaling).
18                resolution: WindowResolution::default().with_scale_factor_override(1.),
19                ..default()
20            }),
21            ..Default::default()
22        }))
23        .add_systems(Startup, setup_scene)
24        .run();
25}
26
27fn setup_scene(mut commands: Commands) {
28    // The first camera; no render target is specified, its render target will be set to the primary window automatically.
29    // This camera has no `RenderLayers` component, so it only renders entities belonging to render layer `0`.
30    commands.spawn(Camera2d);
31
32    // Spawn a second window
33    let secondary_window = commands
34        .spawn(Window {
35            title: "Secondary Window".to_owned(),
36            // Override the secondary window's scale factor and set it to double that of the primary window.
37            // This means the second window's text will use glyphs drawn at twice the resolution of the primary window's text,
38            // and they will be twice as big on screen.
39            resolution: WindowResolution::default().with_scale_factor_override(2.),
40            ..default()
41        })
42        .id();
43
44    // Spawn a second camera
45    let secondary_window_camera = commands
46        .spawn((
47            Camera2d,
48            // This camera will only render entities belonging to render layer `1`.
49            RenderLayers::layer(1),
50            Camera {
51                // Without an explicit render target, this camera would also target the primary window.
52                target: RenderTarget::Window(WindowRef::Entity(secondary_window)),
53                ..default()
54            },
55        ))
56        .id();
57
58    let node = Node {
59        position_type: PositionType::Absolute,
60        top: Val::Px(12.0),
61        left: Val::Px(12.0),
62        ..default()
63    };
64
65    let text_font = TextFont::from_font_size(30.);
66
67    // UI nodes can only be rendered by one camera at a time and ignore `RenderLayers`.
68    // This root UI node has no `UiTargetCamera` so `bevy_ui` will try to find a
69    // camera with the `IsDefaultUiCamera` marker component. When that fails (neither
70    // camera spawned here has an `IsDefaultUiCamera`), it queries for the
71    // first camera targeting the primary window and uses that.
72    commands.spawn(node.clone()).with_child((
73        Text::new("UI Text Primary Window"),
74        text_font.clone(),
75        TextShadow::default(),
76    ));
77
78    commands
79        .spawn((node, UiTargetCamera(secondary_window_camera)))
80        .with_child((
81            Text::new("UI Text Secondary Window"),
82            text_font.clone(),
83            TextShadow::default(),
84        ));
85
86    // `Text2d` belonging to render layer `0`.
87    commands.spawn((
88        Text2d::new("Text2d Primary Window"),
89        TextColor(YELLOW.into()),
90        text_font.clone(),
91        Text2dShadow::default(),
92    ));
93
94    // `Text2d` belonging to render layer `1`.
95    commands.spawn((
96        Text2d::new("Text2d Secondary Window"),
97        TextColor(YELLOW.into()),
98        text_font.clone(),
99        Text2dShadow::default(),
100        RenderLayers::layer(1),
101    ));
102
103    // This `Text2d` entity belongs to both render layers `0` and `1`, so it will be rendered by both
104    // cameras. A single text layout is generated per `Text2d` entity, targeting a specific scale
105    // factor. Since the two camera's render targets have different scale factors, the text layout
106    // will be generated using the higher scale factor (the secondary window's), and then downscaled when it is
107    // drawn by the camera targeting the primary window.
108    commands.spawn((
109        Text2d::new("Text2d Both Windows"),
110        TextColor(LIGHT_CYAN.into()),
111        text_font,
112        Text2dShadow::default(),
113        RenderLayers::from_layers(&[0, 1]),
114        Transform::from_xyz(0., -50., 0.),
115    ));
116}