bevyengine / bevy

A refreshingly simple data-driven game engine built in Rust
https://bevyengine.org
Apache License 2.0
35.14k stars 3.45k forks source link

`JustifyText::Center` doesn't place a text in the center of `Text2dBounds` #14266

Open m2ym opened 2 months ago

m2ym commented 2 months ago

Bevy version

v0.14.0

What you did

use bevy::prelude::*;
use bevy::sprite::Anchor;
use bevy::text::Text2dBounds;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .run();
}

fn setup(mut commands: Commands) {
    commands.spawn(Camera2dBundle::default());

    commands.spawn(Text2dBundle {
        text: Text::from_section(
            "hello",
            TextStyle {
                font_size: 60.0,
                ..default()
            },
        )
        .with_justify(JustifyText::Center),
        text_2d_bounds: Text2dBounds {
            size: Vec2::new(600., 200.),
        },
        text_anchor: Anchor::Center,
        ..default()
    });
}

What went wrong

The text ("hello" in the code above) is not placed at the center, but at the right.

v0_14_0

In v0.13.2, the text is rendered as expected.

v0_13_2

mgi388 commented 2 months ago

@m2ym out of interest does the response https://github.com/bevyengine/bevy/issues/14251#issuecomment-2218808296 help in your case?

ickshonpe commented 2 months ago

This is a bug. JustifyText isn't meant to affect single lines of text. The Anchor component controls Text2d's alignment relative to the position of its transform.

The Text2dBounds constraint seems to be the problem. Without it the text is centered in Bevy 14 like in the second screenshot:

fn setup(mut commands: Commands) {
    commands.spawn(Camera2dBundle::default());

    commands.spawn(Text2dBundle {
        text: Text::from_section(
            "hello",
            TextStyle {
                font_size: 60.0,
                ..default()
            },
        ),
        text_anchor: Anchor::Center,
        ..default()
    });
}
ickshonpe commented 2 months ago

This is really odd:

use bevy::color::palettes;
use bevy::math::vec2;
use bevy::prelude::*;
use bevy::sprite::Anchor;
use bevy::text::Text2dBounds;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .run();
}

fn setup(mut commands: Commands) {
    commands.spawn(Camera2dBundle::default());

    let size = vec2(600., 200.);
    commands.spawn(SpriteBundle {
        sprite: Sprite {
            color: palettes::css::NAVY.into(),
            custom_size: Some(size),
            anchor: Anchor::Center,
            ..Default::default()
        },
        transform: Transform::from_translation(300. * Vec3::Y),

        ..Default::default()
    });

    commands.spawn(SpriteBundle {
        sprite: Sprite {
            color: palettes::css::NAVY.into(),
            custom_size: Some(size),
            anchor: Anchor::Center,
            ..Default::default()
        },
        ..Default::default()
    });

    commands.spawn(Text2dBundle {
        text: Text::from_section(
            "hello",
            TextStyle {
                font_size: 60.0,
                ..default()
            },
        )
        .with_justify(JustifyText::Center),
        text_2d_bounds: Text2dBounds {
            size: Vec2::new(600., 200.),
        },
        text_anchor: Anchor::Center,
        ..default()
    });
}
hello

z-ordering

ickshonpe commented 2 months ago

As a temporary fix you can add this system to PostUpdate after update_text2d_layout:

fn fix_text2d_layout(
    mut query: Query<&mut TextLayoutInfo, (Changed<TextLayoutInfo>, With<Anchor>)>,
) {
    for mut layout_info in query.iter_mut() {
        let mut min_x = f32::MAX;
        for glyph in layout_info.glyphs.iter() {
            min_x = (glyph.position.x - 0.5 * glyph.size.x).min(min_x);
        }

        for glyph in layout_info.glyphs.iter_mut() {
            glyph.position.x -= min_x;
        }
    }
}

Should work with both bevy 0.14 and main.

m2ym commented 2 months ago

@ickshonpe The temporary fix works. Thanks for your work and a very quick response!