Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/testbed/2d.rs
9312 views
1
//! 2d testbed
2
//!
3
//! You can switch scene by pressing the spacebar
4
5
mod helpers;
6
7
use argh::FromArgs;
8
use bevy::prelude::*;
9
10
use helpers::Next;
11
12
#[derive(FromArgs)]
13
/// 2d testbed
14
pub struct Args {
15
#[argh(positional)]
16
scene: Option<Scene>,
17
}
18
19
fn main() {
20
#[cfg(not(target_arch = "wasm32"))]
21
let args: Args = argh::from_env();
22
#[cfg(target_arch = "wasm32")]
23
let args: Args = Args::from_args(&[], &[]).unwrap();
24
25
let mut app = App::new();
26
app.add_plugins((DefaultPlugins,))
27
.add_systems(OnEnter(Scene::Shapes), shapes::setup)
28
.add_systems(OnEnter(Scene::Bloom), bloom::setup)
29
.add_systems(OnEnter(Scene::Text), text::setup)
30
.add_systems(OnEnter(Scene::Sprite), sprite::setup)
31
.add_systems(OnEnter(Scene::SpriteSlicing), sprite_slicing::setup)
32
.add_systems(OnEnter(Scene::Gizmos), gizmos::setup)
33
.add_systems(Update, switch_scene)
34
.add_systems(Update, gizmos::draw_gizmos.run_if(in_state(Scene::Gizmos)));
35
36
match args.scene {
37
None => app.init_state::<Scene>(),
38
Some(scene) => app.insert_state(scene),
39
};
40
41
#[cfg(feature = "bevy_ci_testing")]
42
app.add_systems(Update, helpers::switch_scene_in_ci::<Scene>);
43
44
app.run();
45
}
46
47
#[derive(Debug, Clone, Eq, PartialEq, Hash, States, Default)]
48
enum Scene {
49
#[default]
50
Shapes,
51
Bloom,
52
Text,
53
Sprite,
54
SpriteSlicing,
55
Gizmos,
56
}
57
58
impl std::str::FromStr for Scene {
59
type Err = String;
60
61
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
62
let mut isit = Self::default();
63
while s.to_lowercase() != format!("{isit:?}").to_lowercase() {
64
isit = isit.next();
65
if isit == Self::default() {
66
return Err(format!("Invalid Scene name: {s}"));
67
}
68
}
69
Ok(isit)
70
}
71
}
72
73
impl Next for Scene {
74
fn next(&self) -> Self {
75
match self {
76
Scene::Shapes => Scene::Bloom,
77
Scene::Bloom => Scene::Text,
78
Scene::Text => Scene::Sprite,
79
Scene::Sprite => Scene::SpriteSlicing,
80
Scene::SpriteSlicing => Scene::Gizmos,
81
Scene::Gizmos => Scene::Shapes,
82
}
83
}
84
}
85
86
fn switch_scene(
87
keyboard: Res<ButtonInput<KeyCode>>,
88
scene: Res<State<Scene>>,
89
mut next_scene: ResMut<NextState<Scene>>,
90
) {
91
if keyboard.just_pressed(KeyCode::Space) {
92
info!("Switching scene");
93
next_scene.set(scene.get().next());
94
}
95
}
96
97
mod shapes {
98
use bevy::prelude::*;
99
100
const X_EXTENT: f32 = 900.;
101
102
pub fn setup(
103
mut commands: Commands,
104
mut meshes: ResMut<Assets<Mesh>>,
105
mut materials: ResMut<Assets<ColorMaterial>>,
106
) {
107
commands.spawn((Camera2d, DespawnOnExit(super::Scene::Shapes)));
108
109
let shapes = [
110
meshes.add(Circle::new(50.0)),
111
meshes.add(CircularSector::new(50.0, 1.0)),
112
meshes.add(CircularSegment::new(50.0, 1.25)),
113
meshes.add(Ellipse::new(25.0, 50.0)),
114
meshes.add(Annulus::new(25.0, 50.0)),
115
meshes.add(Capsule2d::new(25.0, 50.0)),
116
meshes.add(Rhombus::new(75.0, 100.0)),
117
meshes.add(Rectangle::new(50.0, 100.0)),
118
meshes.add(RegularPolygon::new(50.0, 6)),
119
meshes.add(Triangle2d::new(
120
Vec2::Y * 50.0,
121
Vec2::new(-50.0, -50.0),
122
Vec2::new(50.0, -50.0),
123
)),
124
];
125
let num_shapes = shapes.len();
126
127
for (i, shape) in shapes.into_iter().enumerate() {
128
// Distribute colors evenly across the rainbow.
129
let color = Color::hsl(360. * i as f32 / num_shapes as f32, 0.95, 0.7);
130
131
commands.spawn((
132
Mesh2d(shape),
133
MeshMaterial2d(materials.add(color)),
134
Transform::from_xyz(
135
// Distribute shapes from -X_EXTENT/2 to +X_EXTENT/2.
136
-X_EXTENT / 2. + i as f32 / (num_shapes - 1) as f32 * X_EXTENT,
137
0.0,
138
0.0,
139
),
140
DespawnOnExit(super::Scene::Shapes),
141
));
142
}
143
}
144
}
145
146
mod bloom {
147
use bevy::{core_pipeline::tonemapping::Tonemapping, post_process::bloom::Bloom, prelude::*};
148
149
pub fn setup(
150
mut commands: Commands,
151
mut meshes: ResMut<Assets<Mesh>>,
152
mut materials: ResMut<Assets<ColorMaterial>>,
153
) {
154
commands.spawn((
155
Camera2d,
156
Tonemapping::TonyMcMapface,
157
Bloom::default(),
158
DespawnOnExit(super::Scene::Bloom),
159
));
160
161
commands.spawn((
162
Mesh2d(meshes.add(Circle::new(100.))),
163
MeshMaterial2d(materials.add(Color::srgb(7.5, 0.0, 7.5))),
164
Transform::from_translation(Vec3::new(-200., 0., 0.)),
165
DespawnOnExit(super::Scene::Bloom),
166
));
167
168
commands.spawn((
169
Mesh2d(meshes.add(RegularPolygon::new(100., 6))),
170
MeshMaterial2d(materials.add(Color::srgb(6.25, 9.4, 9.1))),
171
Transform::from_translation(Vec3::new(200., 0., 0.)),
172
DespawnOnExit(super::Scene::Bloom),
173
));
174
}
175
}
176
177
mod text {
178
use bevy::color::palettes;
179
use bevy::prelude::*;
180
use bevy::sprite::Anchor;
181
use bevy::text::TextBounds;
182
183
pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
184
commands.spawn((Camera2d, DespawnOnExit(super::Scene::Text)));
185
186
for (i, justify) in [
187
Justify::Left,
188
Justify::Right,
189
Justify::Center,
190
Justify::Justified,
191
]
192
.into_iter()
193
.enumerate()
194
{
195
let y = 230. - 150. * i as f32;
196
spawn_anchored_text(&mut commands, -300. * Vec3::X + y * Vec3::Y, justify, None);
197
spawn_anchored_text(
198
&mut commands,
199
300. * Vec3::X + y * Vec3::Y,
200
justify,
201
Some(TextBounds::new(150., 60.)),
202
);
203
}
204
205
let sans_serif = TextFont::from(asset_server.load("fonts/FiraSans-Bold.ttf"));
206
207
const NUM_ITERATIONS: usize = 10;
208
for i in 0..NUM_ITERATIONS {
209
let fraction = i as f32 / (NUM_ITERATIONS - 1) as f32;
210
211
commands.spawn((
212
Text2d::new("Bevy"),
213
sans_serif.clone(),
214
Transform::from_xyz(0.0, fraction * 200.0, i as f32)
215
.with_scale(1.0 + Vec2::splat(fraction).extend(1.))
216
.with_rotation(Quat::from_rotation_z(fraction * core::f32::consts::PI)),
217
TextColor(Color::hsla(fraction * 360.0, 0.8, 0.8, 0.8)),
218
DespawnOnExit(super::Scene::Text),
219
));
220
}
221
222
commands.spawn((
223
Text2d::new("This text is invisible."),
224
Visibility::Hidden,
225
DespawnOnExit(super::Scene::Text),
226
));
227
}
228
229
fn spawn_anchored_text(
230
commands: &mut Commands,
231
dest: Vec3,
232
justify: Justify,
233
bounds: Option<TextBounds>,
234
) {
235
commands.spawn((
236
Sprite {
237
color: palettes::css::YELLOW.into(),
238
custom_size: Some(5. * Vec2::ONE),
239
..Default::default()
240
},
241
Transform::from_translation(dest),
242
DespawnOnExit(super::Scene::Text),
243
));
244
245
for anchor in [
246
Anchor::TOP_LEFT,
247
Anchor::TOP_RIGHT,
248
Anchor::BOTTOM_RIGHT,
249
Anchor::BOTTOM_LEFT,
250
] {
251
let mut text = commands.spawn((
252
Text2d::new("L R\n"),
253
TextLayout::new_with_justify(justify),
254
Transform::from_translation(dest + Vec3::Z),
255
anchor,
256
DespawnOnExit(super::Scene::Text),
257
ShowAabbGizmo {
258
color: Some(palettes::tailwind::AMBER_400.into()),
259
},
260
children![
261
(
262
TextSpan::new(format!("{}, {}\n", anchor.x, anchor.y)),
263
TextFont::from_font_size(14.0),
264
TextColor(palettes::tailwind::BLUE_400.into()),
265
),
266
(
267
TextSpan::new(format!("{justify:?}")),
268
TextFont::from_font_size(14.0),
269
TextColor(palettes::tailwind::GREEN_400.into()),
270
),
271
],
272
));
273
if let Some(bounds) = bounds {
274
text.insert(bounds);
275
276
commands.spawn((
277
Sprite {
278
color: palettes::tailwind::GRAY_900.into(),
279
custom_size: Some(Vec2::new(bounds.width.unwrap(), bounds.height.unwrap())),
280
..Default::default()
281
},
282
Transform::from_translation(dest - Vec3::Z),
283
anchor,
284
DespawnOnExit(super::Scene::Text),
285
));
286
}
287
}
288
}
289
}
290
291
mod sprite {
292
use bevy::color::palettes::css::{BLUE, LIME, RED};
293
use bevy::prelude::*;
294
use bevy::sprite::Anchor;
295
296
pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
297
commands.spawn((Camera2d, DespawnOnExit(super::Scene::Sprite)));
298
for (anchor, flip_x, flip_y, color) in [
299
(Anchor::BOTTOM_LEFT, false, false, Color::WHITE),
300
(Anchor::BOTTOM_RIGHT, true, false, RED.into()),
301
(Anchor::TOP_LEFT, false, true, LIME.into()),
302
(Anchor::TOP_RIGHT, true, true, BLUE.into()),
303
] {
304
commands.spawn((
305
Sprite {
306
image: asset_server.load("branding/bevy_logo_dark.png"),
307
flip_x,
308
flip_y,
309
color,
310
..default()
311
},
312
anchor,
313
DespawnOnExit(super::Scene::Sprite),
314
));
315
}
316
}
317
}
318
319
mod sprite_slicing {
320
use bevy::prelude::*;
321
use bevy::sprite::{BorderRect, SliceScaleMode, SpriteImageMode, TextureSlicer};
322
323
pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
324
commands.spawn((Camera2d, DespawnOnExit(super::Scene::SpriteSlicing)));
325
326
let texture = asset_server.load("textures/slice_square_2.png");
327
let font = asset_server.load("fonts/FiraSans-Bold.ttf");
328
329
commands.spawn((
330
Sprite {
331
image: texture.clone(),
332
..default()
333
},
334
Transform::from_translation(Vec3::new(-150.0, 50.0, 0.0)).with_scale(Vec3::splat(2.0)),
335
DespawnOnExit(super::Scene::SpriteSlicing),
336
));
337
338
commands.spawn((
339
Sprite {
340
image: texture,
341
image_mode: SpriteImageMode::Sliced(TextureSlicer {
342
border: BorderRect::all(20.0),
343
center_scale_mode: SliceScaleMode::Stretch,
344
..default()
345
}),
346
custom_size: Some(Vec2::new(200.0, 200.0)),
347
..default()
348
},
349
Transform::from_translation(Vec3::new(150.0, 50.0, 0.0)),
350
DespawnOnExit(super::Scene::SpriteSlicing),
351
));
352
353
commands.spawn((
354
Text2d::new("Original"),
355
TextFont {
356
font: FontSource::from(font.clone()),
357
font_size: FontSize::Px(20.0),
358
..default()
359
},
360
Transform::from_translation(Vec3::new(-150.0, -80.0, 0.0)),
361
DespawnOnExit(super::Scene::SpriteSlicing),
362
));
363
364
commands.spawn((
365
Text2d::new("Sliced"),
366
TextFont {
367
font: FontSource::from(font.clone()),
368
font_size: FontSize::Px(20.0),
369
..default()
370
},
371
Transform::from_translation(Vec3::new(150.0, -80.0, 0.0)),
372
DespawnOnExit(super::Scene::SpriteSlicing),
373
));
374
}
375
}
376
377
mod gizmos {
378
use bevy::{color::palettes::css::*, prelude::*};
379
380
pub fn setup(mut commands: Commands) {
381
commands.spawn((Camera2d, DespawnOnExit(super::Scene::Gizmos)));
382
}
383
384
pub fn draw_gizmos(mut gizmos: Gizmos) {
385
gizmos.rect_2d(
386
Isometry2d::from_translation(Vec2::new(-200.0, 0.0)),
387
Vec2::new(200.0, 200.0),
388
RED,
389
);
390
gizmos
391
.circle_2d(
392
Isometry2d::from_translation(Vec2::new(-200.0, 0.0)),
393
200.0,
394
GREEN,
395
)
396
.resolution(64);
397
398
// 2d grids with all variations of outer edges on or off
399
for i in 0..4 {
400
let x = 200.0 * (1.0 + (i % 2) as f32);
401
let y = 150.0 * (0.5 - (i / 2) as f32);
402
let mut grid = gizmos.grid(
403
Vec3::new(x, y, 0.0),
404
UVec2::new(5, 4),
405
Vec2::splat(30.),
406
Color::WHITE,
407
);
408
if i & 1 > 0 {
409
grid = grid.outer_edges_x();
410
}
411
if i & 2 > 0 {
412
grid.outer_edges_y();
413
}
414
}
415
}
416
}
417
418