1mod helpers;
6
7use bevy::prelude::*;
8use helpers::Next;
9
10fn main() {
11 let mut app = App::new();
12 app.add_plugins((DefaultPlugins,))
13 .init_state::<Scene>()
14 .add_systems(OnEnter(Scene::Shapes), shapes::setup)
15 .add_systems(OnEnter(Scene::Bloom), bloom::setup)
16 .add_systems(OnEnter(Scene::Text), text::setup)
17 .add_systems(OnEnter(Scene::Sprite), sprite::setup)
18 .add_systems(OnEnter(Scene::Gizmos), gizmos::setup)
19 .add_systems(Update, switch_scene)
20 .add_systems(Update, gizmos::draw_gizmos.run_if(in_state(Scene::Gizmos)));
21
22 #[cfg(feature = "bevy_ci_testing")]
23 app.add_systems(Update, helpers::switch_scene_in_ci::<Scene>);
24
25 app.run();
26}
27
28#[derive(Debug, Clone, Eq, PartialEq, Hash, States, Default)]
29enum Scene {
30 #[default]
31 Shapes,
32 Bloom,
33 Text,
34 Sprite,
35 Gizmos,
36}
37
38impl Next for Scene {
39 fn next(&self) -> Self {
40 match self {
41 Scene::Shapes => Scene::Bloom,
42 Scene::Bloom => Scene::Text,
43 Scene::Text => Scene::Sprite,
44 Scene::Sprite => Scene::Gizmos,
45 Scene::Gizmos => Scene::Shapes,
46 }
47 }
48}
49
50fn switch_scene(
51 keyboard: Res<ButtonInput<KeyCode>>,
52 scene: Res<State<Scene>>,
53 mut next_scene: ResMut<NextState<Scene>>,
54) {
55 if keyboard.just_pressed(KeyCode::Space) {
56 info!("Switching scene");
57 next_scene.set(scene.get().next());
58 }
59}
60
61mod shapes {
62 use bevy::prelude::*;
63
64 const X_EXTENT: f32 = 900.;
65
66 pub fn setup(
67 mut commands: Commands,
68 mut meshes: ResMut<Assets<Mesh>>,
69 mut materials: ResMut<Assets<ColorMaterial>>,
70 ) {
71 commands.spawn((Camera2d, DespawnOnExit(super::Scene::Shapes)));
72
73 let shapes = [
74 meshes.add(Circle::new(50.0)),
75 meshes.add(CircularSector::new(50.0, 1.0)),
76 meshes.add(CircularSegment::new(50.0, 1.25)),
77 meshes.add(Ellipse::new(25.0, 50.0)),
78 meshes.add(Annulus::new(25.0, 50.0)),
79 meshes.add(Capsule2d::new(25.0, 50.0)),
80 meshes.add(Rhombus::new(75.0, 100.0)),
81 meshes.add(Rectangle::new(50.0, 100.0)),
82 meshes.add(RegularPolygon::new(50.0, 6)),
83 meshes.add(Triangle2d::new(
84 Vec2::Y * 50.0,
85 Vec2::new(-50.0, -50.0),
86 Vec2::new(50.0, -50.0),
87 )),
88 ];
89 let num_shapes = shapes.len();
90
91 for (i, shape) in shapes.into_iter().enumerate() {
92 let color = Color::hsl(360. * i as f32 / num_shapes as f32, 0.95, 0.7);
94
95 commands.spawn((
96 Mesh2d(shape),
97 MeshMaterial2d(materials.add(color)),
98 Transform::from_xyz(
99 -X_EXTENT / 2. + i as f32 / (num_shapes - 1) as f32 * X_EXTENT,
101 0.0,
102 0.0,
103 ),
104 DespawnOnExit(super::Scene::Shapes),
105 ));
106 }
107 }
108}
109
110mod bloom {
111 use bevy::{core_pipeline::tonemapping::Tonemapping, post_process::bloom::Bloom, prelude::*};
112
113 pub fn setup(
114 mut commands: Commands,
115 mut meshes: ResMut<Assets<Mesh>>,
116 mut materials: ResMut<Assets<ColorMaterial>>,
117 ) {
118 commands.spawn((
119 Camera2d,
120 Tonemapping::TonyMcMapface,
121 Bloom::default(),
122 DespawnOnExit(super::Scene::Bloom),
123 ));
124
125 commands.spawn((
126 Mesh2d(meshes.add(Circle::new(100.))),
127 MeshMaterial2d(materials.add(Color::srgb(7.5, 0.0, 7.5))),
128 Transform::from_translation(Vec3::new(-200., 0., 0.)),
129 DespawnOnExit(super::Scene::Bloom),
130 ));
131
132 commands.spawn((
133 Mesh2d(meshes.add(RegularPolygon::new(100., 6))),
134 MeshMaterial2d(materials.add(Color::srgb(6.25, 9.4, 9.1))),
135 Transform::from_translation(Vec3::new(200., 0., 0.)),
136 DespawnOnExit(super::Scene::Bloom),
137 ));
138 }
139}
140
141mod text {
142 use bevy::color::palettes;
143 use bevy::prelude::*;
144 use bevy::sprite::Anchor;
145 use bevy::text::TextBounds;
146
147 pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
148 commands.spawn((Camera2d, DespawnOnExit(super::Scene::Text)));
149
150 for (i, justify) in [
151 Justify::Left,
152 Justify::Right,
153 Justify::Center,
154 Justify::Justified,
155 ]
156 .into_iter()
157 .enumerate()
158 {
159 let y = 230. - 150. * i as f32;
160 spawn_anchored_text(&mut commands, -300. * Vec3::X + y * Vec3::Y, justify, None);
161 spawn_anchored_text(
162 &mut commands,
163 300. * Vec3::X + y * Vec3::Y,
164 justify,
165 Some(TextBounds::new(150., 60.)),
166 );
167 }
168
169 let sans_serif = TextFont::from(asset_server.load("fonts/FiraSans-Bold.ttf"));
170
171 const NUM_ITERATIONS: usize = 10;
172 for i in 0..NUM_ITERATIONS {
173 let fraction = i as f32 / (NUM_ITERATIONS - 1) as f32;
174
175 commands.spawn((
176 Text2d::new("Bevy"),
177 sans_serif.clone(),
178 Transform::from_xyz(0.0, fraction * 200.0, i as f32)
179 .with_scale(1.0 + Vec2::splat(fraction).extend(1.))
180 .with_rotation(Quat::from_rotation_z(fraction * core::f32::consts::PI)),
181 TextColor(Color::hsla(fraction * 360.0, 0.8, 0.8, 0.8)),
182 DespawnOnExit(super::Scene::Text),
183 ));
184 }
185
186 commands.spawn((
187 Text2d::new("This text is invisible."),
188 Visibility::Hidden,
189 DespawnOnExit(super::Scene::Text),
190 ));
191 }
192
193 fn spawn_anchored_text(
194 commands: &mut Commands,
195 dest: Vec3,
196 justify: Justify,
197 bounds: Option<TextBounds>,
198 ) {
199 commands.spawn((
200 Sprite {
201 color: palettes::css::YELLOW.into(),
202 custom_size: Some(5. * Vec2::ONE),
203 ..Default::default()
204 },
205 Transform::from_translation(dest),
206 DespawnOnExit(super::Scene::Text),
207 ));
208
209 for anchor in [
210 Anchor::TOP_LEFT,
211 Anchor::TOP_RIGHT,
212 Anchor::BOTTOM_RIGHT,
213 Anchor::BOTTOM_LEFT,
214 ] {
215 let mut text = commands.spawn((
216 Text2d::new("L R\n"),
217 TextLayout::new_with_justify(justify),
218 Transform::from_translation(dest + Vec3::Z),
219 anchor,
220 DespawnOnExit(super::Scene::Text),
221 ShowAabbGizmo {
222 color: Some(palettes::tailwind::AMBER_400.into()),
223 },
224 children![
225 (
226 TextSpan::new(format!("{}, {}\n", anchor.x, anchor.y)),
227 TextFont::from_font_size(14.0),
228 TextColor(palettes::tailwind::BLUE_400.into()),
229 ),
230 (
231 TextSpan::new(format!("{justify:?}")),
232 TextFont::from_font_size(14.0),
233 TextColor(palettes::tailwind::GREEN_400.into()),
234 ),
235 ],
236 ));
237 if let Some(bounds) = bounds {
238 text.insert(bounds);
239
240 commands.spawn((
241 Sprite {
242 color: palettes::tailwind::GRAY_900.into(),
243 custom_size: Some(Vec2::new(bounds.width.unwrap(), bounds.height.unwrap())),
244 ..Default::default()
245 },
246 Transform::from_translation(dest - Vec3::Z),
247 anchor,
248 DespawnOnExit(super::Scene::Text),
249 ));
250 }
251 }
252 }
253}
254
255mod sprite {
256 use bevy::color::palettes::css::{BLUE, LIME, RED};
257 use bevy::prelude::*;
258 use bevy::sprite::Anchor;
259
260 pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
261 commands.spawn((Camera2d, DespawnOnExit(super::Scene::Sprite)));
262 for (anchor, flip_x, flip_y, color) in [
263 (Anchor::BOTTOM_LEFT, false, false, Color::WHITE),
264 (Anchor::BOTTOM_RIGHT, true, false, RED.into()),
265 (Anchor::TOP_LEFT, false, true, LIME.into()),
266 (Anchor::TOP_RIGHT, true, true, BLUE.into()),
267 ] {
268 commands.spawn((
269 Sprite {
270 image: asset_server.load("branding/bevy_logo_dark.png"),
271 flip_x,
272 flip_y,
273 color,
274 ..default()
275 },
276 anchor,
277 DespawnOnExit(super::Scene::Sprite),
278 ));
279 }
280 }
281}
282
283mod gizmos {
284 use bevy::{color::palettes::css::*, prelude::*};
285
286 pub fn setup(mut commands: Commands) {
287 commands.spawn((Camera2d, DespawnOnExit(super::Scene::Gizmos)));
288 }
289
290 pub fn draw_gizmos(mut gizmos: Gizmos) {
291 gizmos.rect_2d(
292 Isometry2d::from_translation(Vec2::new(-200.0, 0.0)),
293 Vec2::new(200.0, 200.0),
294 RED,
295 );
296 gizmos
297 .circle_2d(
298 Isometry2d::from_translation(Vec2::new(-200.0, 0.0)),
299 200.0,
300 GREEN,
301 )
302 .resolution(64);
303
304 for i in 0..4 {
306 let x = 200.0 * (1.0 + (i % 2) as f32);
307 let y = 150.0 * (0.5 - (i / 2) as f32);
308 let mut grid = gizmos.grid(
309 Vec3::new(x, y, 0.0),
310 UVec2::new(5, 4),
311 Vec2::splat(30.),
312 Color::WHITE,
313 );
314 if i & 1 > 0 {
315 grid = grid.outer_edges_x();
316 }
317 if i & 2 > 0 {
318 grid.outer_edges_y();
319 }
320 }
321 }
322}