docs/bevy/examples/2d/text2d.rs 6.9 K · 206 lines · raw · history

1 //! Shows text rendering with moving, rotating and scaling text.
2 //!
3 //! Note that this uses [`Text2d`] to display text alongside your other entities in a 2D scene.
4 //!
5 //! For an example on how to render text as part of a user interface, independent from the world
6 //! viewport, you may want to look at `showcase/contributors.rs` or `ui/text.rs`.
7
8 use bevy::{
9 color::palettes::css::*,
10 math::ops,
11 prelude::*,
12 sprite::{Anchor, Text2dShadow},
13 text::{FontSmoothing, LineBreak, TextBounds},
14 };
15
16 fn main() {
17 App::new()
18 .add_plugins(DefaultPlugins)
19 .add_systems(Startup, setup)
20 .add_systems(
21 Update,
22 (animate_translation, animate_rotation, animate_scale),
23 )
24 .run();
25 }
26
27 #[derive(Component)]
28 struct AnimateTranslation;
29
30 #[derive(Component)]
31 struct AnimateRotation;
32
33 #[derive(Component)]
34 struct AnimateScale;
35
36 fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
37 let font = asset_server.load("fonts/FiraSans-Bold.ttf");
38 let text_font = TextFont {
39 font: font.clone().into(),
40 font_size: FontSize::Px(50.0),
41 ..default()
42 };
43 let text_justification = Justify::Center;
44 commands.spawn(Camera2d);
45 // Demonstrate changing translation
46 commands.spawn((
47 Text2d::new(" translation "),
48 text_font.clone(),
49 TextLayout::justify(text_justification),
50 TextBackgroundColor(Color::BLACK.with_alpha(0.5)),
51 Text2dShadow::default(),
52 AnimateTranslation,
53 ));
54 // Demonstrate changing rotation
55 commands.spawn((
56 Text2d::new(" rotation "),
57 text_font.clone(),
58 TextLayout::justify(text_justification),
59 TextBackgroundColor(Color::BLACK.with_alpha(0.5)),
60 Text2dShadow::default(),
61 AnimateRotation,
62 ));
63 // Demonstrate changing scale
64 commands.spawn((
65 Text2d::new(" scale "),
66 text_font,
67 TextLayout::justify(text_justification),
68 Transform::from_translation(Vec3::new(400.0, 0.0, 0.0)),
69 TextBackgroundColor(Color::BLACK.with_alpha(0.5)),
70 Text2dShadow::default(),
71 AnimateScale,
72 ));
73 // Demonstrate text wrapping
74 let slightly_smaller_text_font = TextFont {
75 font: font.into(),
76 font_size: FontSize::Px(35.0),
77 ..default()
78 };
79 let box_size = Vec2::new(300.0, 200.0);
80 let box_position = Vec2::new(0.0, -250.0);
81 let box_color = Color::srgb(0.25, 0.25, 0.55);
82 let text_shadow_color = box_color.darker(0.05);
83 commands.spawn((
84 Sprite::from_color(Color::srgb(0.25, 0.25, 0.55), box_size),
85 Transform::from_translation(box_position.extend(0.0)),
86 children![(
87 Text2d::new("this text wraps in the box\n(Unicode linebreaks)"),
88 slightly_smaller_text_font.clone(),
89 TextLayout::new(Justify::Left, LineBreak::WordBoundary),
90 // Wrap text in the rectangle
91 TextBounds::from(box_size),
92 // Ensure the text is drawn on top of the box
93 Transform::from_translation(Vec3::Z),
94 // Add a shadow to the text
95 Text2dShadow {
96 color: text_shadow_color,
97 ..default()
98 },
99 Underline,
100 )],
101 ));
102
103 let other_box_size = Vec2::new(300.0, 200.0);
104 let other_box_position = Vec2::new(320.0, -250.0);
105 commands.spawn((
106 Sprite::from_color(Color::srgb(0.25, 0.25, 0.55), other_box_size),
107 Transform::from_translation(other_box_position.extend(0.0)),
108 children![(
109 Text2d::new("this text wraps in the box\n(AnyCharacter linebreaks)"),
110 slightly_smaller_text_font.clone(),
111 TextLayout::new(Justify::Left, LineBreak::AnyCharacter),
112 // Wrap text in the rectangle
113 TextBounds::from(other_box_size),
114 // Ensure the text is drawn on top of the box
115 Transform::from_translation(Vec3::Z),
116 // Add a shadow to the text
117 Text2dShadow {
118 color: text_shadow_color,
119 ..default()
120 }
121 )],
122 ));
123
124 // Demonstrate font smoothing off
125 commands.spawn((
126 Text2d::new("This text has\nFontSmoothing::None\nAnd Justify::Center"),
127 slightly_smaller_text_font
128 .clone()
129 .with_font_smoothing(FontSmoothing::None),
130 TextLayout::justify(Justify::Center),
131 Transform::from_translation(Vec3::new(-400.0, -250.0, 0.0)),
132 // Add a black shadow to the text
133 Text2dShadow::default(),
134 ));
135
136 let make_child = move |(text_anchor, color): (Anchor, Color)| {
137 (
138 Text2d::new(" Anchor".to_string()),
139 slightly_smaller_text_font.clone(),
140 text_anchor,
141 TextBackgroundColor(Color::WHITE.darker(0.8)),
142 Transform::from_translation(-1. * Vec3::Z),
143 children![
144 (
145 TextSpan("::".to_string()),
146 slightly_smaller_text_font.clone(),
147 TextColor(LIGHT_GREY.into()),
148 TextBackgroundColor(DARK_BLUE.into()),
149 ),
150 (
151 TextSpan(format!("{text_anchor:?} ")),
152 slightly_smaller_text_font.clone(),
153 TextColor(color),
154 TextBackgroundColor(color.darker(0.3)),
155 )
156 ],
157 )
158 };
159
160 commands.spawn((
161 Sprite {
162 color: Color::Srgba(LIGHT_CYAN),
163 custom_size: Some(Vec2::new(10., 10.)),
164 ..Default::default()
165 },
166 Transform::from_translation(250. * Vec3::Y),
167 children![
168 make_child((Anchor::TOP_LEFT, Color::Srgba(LIGHT_SALMON))),
169 make_child((Anchor::TOP_RIGHT, Color::Srgba(LIGHT_GREEN))),
170 make_child((Anchor::BOTTOM_RIGHT, Color::Srgba(LIGHT_BLUE))),
171 make_child((Anchor::BOTTOM_LEFT, Color::Srgba(LIGHT_YELLOW))),
172 ],
173 ));
174 }
175
176 fn animate_translation(
177 time: Res<Time>,
178 mut query: Query<&mut Transform, (With<Text2d>, With<AnimateTranslation>)>,
179 ) {
180 for mut transform in &mut query {
181 transform.translation.x = 100.0 * ops::sin(time.elapsed_secs()) - 400.0;
182 transform.translation.y = 100.0 * ops::cos(time.elapsed_secs());
183 }
184 }
185
186 fn animate_rotation(
187 time: Res<Time>,
188 mut query: Query<&mut Transform, (With<Text2d>, With<AnimateRotation>)>,
189 ) {
190 for mut transform in &mut query {
191 transform.rotation = Quat::from_rotation_z(ops::cos(time.elapsed_secs()));
192 }
193 }
194
195 fn animate_scale(
196 time: Res<Time>,
197 mut query: Query<&mut Transform, (With<Text2d>, With<AnimateScale>)>,
198 ) {
199 // Consider changing font-size instead of scaling the transform. Scaling a Text2D will scale the
200 // rendered quad, resulting in a pixellated look.
201 for mut transform in &mut query {
202 let scale = (ops::sin(time.elapsed_secs()) + 1.1) * 2.0;
203 transform.scale.x = scale;
204 transform.scale.y = scale;
205 }
206 }