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}