2D Rendering / Multi-Window Text

Back to examples View in GitHub

Renders text to multiple windows with different scale factors using both Text and Text2d.

use bevy::{
    camera::{visibility::RenderLayers, RenderTarget},
    color::palettes::css::{LIGHT_CYAN, YELLOW},
    prelude::*,
    sprite::Text2dShadow,
    window::{WindowRef, WindowResolution},
};

fn main() {
    App::new()
        // By default, a primary window is spawned by `WindowPlugin`, contained in `DefaultPlugins`.
        // The primary window is given the `PrimaryWindow` marker component.
        .add_plugins(DefaultPlugins.set(WindowPlugin {
            primary_window: Some(Window {
                title: "Primary window".to_owned(),
                // Override the primary window's scale factor and use `1.` (no scaling).
                resolution: WindowResolution::default().with_scale_factor_override(1.),
                ..default()
            }),
            ..Default::default()
        }))
        .add_systems(Startup, setup_scene)
        .run();
}

fn setup_scene(mut commands: Commands) {
    // The first camera; no render target is specified, its render target will be set to the primary window automatically.
    // This camera has no `RenderLayers` component, so it only renders entities belonging to render layer `0`.
    commands.spawn(Camera2d);

    // Spawn a second window
    let secondary_window = commands
        .spawn(Window {
            title: "Secondary Window".to_owned(),
            // Override the secondary window's scale factor and set it to double that of the primary window.
            // This means the second window's text will use glyphs drawn at twice the resolution of the primary window's text,
            // and they will be twice as big on screen.
            resolution: WindowResolution::default().with_scale_factor_override(2.),
            ..default()
        })
        .id();

    // Spawn a second camera
    let secondary_window_camera = commands
        .spawn((
            Camera2d,
            // This camera will only render entities belonging to render layer `1`.
            RenderLayers::layer(1),
            Camera {
                // Without an explicit render target, this camera would also target the primary window.
                target: RenderTarget::Window(WindowRef::Entity(secondary_window)),
                ..default()
            },
        ))
        .id();

    let node = Node {
        position_type: PositionType::Absolute,
        top: Val::Px(12.0),
        left: Val::Px(12.0),
        ..default()
    };

    let text_font = TextFont::from_font_size(30.);

    // UI nodes can only be rendered by one camera at a time and ignore `RenderLayers`.
    // This root UI node has no `UiTargetCamera` so `bevy_ui` will try to find a
    // camera with the `IsDefaultUiCamera` marker component. When that fails (neither
    // camera spawned here has an `IsDefaultUiCamera`), it queries for the
    // first camera targeting the primary window and uses that.
    commands.spawn(node.clone()).with_child((
        Text::new("UI Text Primary Window"),
        text_font.clone(),
        TextShadow::default(),
    ));

    commands
        .spawn((node, UiTargetCamera(secondary_window_camera)))
        .with_child((
            Text::new("UI Text Secondary Window"),
            text_font.clone(),
            TextShadow::default(),
        ));

    // `Text2d` belonging to render layer `0`.
    commands.spawn((
        Text2d::new("Text2d Primary Window"),
        TextColor(YELLOW.into()),
        text_font.clone(),
        Text2dShadow::default(),
    ));

    // `Text2d` belonging to render layer `1`.
    commands.spawn((
        Text2d::new("Text2d Secondary Window"),
        TextColor(YELLOW.into()),
        text_font.clone(),
        Text2dShadow::default(),
        RenderLayers::layer(1),
    ));

    // This `Text2d` entity belongs to both render layers `0` and `1`, so it will be rendered by both
    // cameras. A single text layout is generated per `Text2d` entity, targeting a specific scale
    // factor. Since the two camera's render targets have different scale factors, the text layout
    // will be generated using the higher scale factor (the secondary window's), and then downscaled when it is
    // drawn by the camera targeting the primary window.
    commands.spawn((
        Text2d::new("Text2d Both Windows"),
        TextColor(LIGHT_CYAN.into()),
        text_font,
        Text2dShadow::default(),
        RenderLayers::from_layers(&[0, 1]),
        Transform::from_xyz(0., -50., 0.),
    ));
}