Skip to main content

many_sprite_meshes/
many_sprite_meshes.rs

1//! Renders a lot of sprites to allow performance testing.
2//! See <https://github.com/bevyengine/bevy/pull/1492>
3//!
4//! This example sets up many sprites in different sizes, rotations, and scales in the world.
5//! It also moves the camera over them to see how well frustum culling works.
6//!
7//! Add the `--colored` arg to run with color tinted sprites. This will cause the sprites to be rendered
8//! in multiple batches, reducing performance but useful for testing.
9//!
10//! This is a modified version of `many_sprites` but based on [`SpriteMesh`]
11
12use bevy::{
13    color::palettes::css::*,
14    diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
15    prelude::*,
16    window::{PresentMode, WindowResolution},
17    winit::WinitSettings,
18};
19
20use rand::RngExt;
21
22const CAMERA_SPEED: f32 = 1000.0;
23
24const COLORS: [Color; 3] = [Color::Srgba(BLUE), Color::Srgba(WHITE), Color::Srgba(RED)];
25
26#[derive(Resource)]
27struct ColorTint(bool);
28
29fn main() {
30    App::new()
31        .insert_resource(ColorTint(
32            std::env::args().nth(1).unwrap_or_default() == "--colored",
33        ))
34        // Since this is also used as a benchmark, we want it to display performance data.
35        .add_plugins((
36            LogDiagnosticsPlugin::default(),
37            FrameTimeDiagnosticsPlugin::default(),
38            DefaultPlugins.set(WindowPlugin {
39                primary_window: Some(Window {
40                    present_mode: PresentMode::AutoNoVsync,
41                    resolution: WindowResolution::new(1920, 1080).with_scale_factor_override(1.0),
42                    ..default()
43                }),
44                ..default()
45            }),
46        ))
47        .insert_resource(WinitSettings::continuous())
48        .add_systems(Startup, setup)
49        .add_systems(
50            Update,
51            (print_sprite_count, move_camera.after(print_sprite_count)),
52        )
53        .run();
54}
55
56fn setup(mut commands: Commands, assets: Res<AssetServer>, color_tint: Res<ColorTint>) {
57    warn!(include_str!("warning_string.txt"));
58
59    let mut rng = rand::rng();
60
61    let tile_size = Vec2::splat(64.0);
62    let map_size = Vec2::splat(320.0);
63
64    let half_x = (map_size.x / 2.0) as i32;
65    let half_y = (map_size.y / 2.0) as i32;
66
67    let sprite_handle = assets.load("branding/icon.png");
68
69    // Spawns the camera
70
71    commands.spawn(Camera2d);
72
73    // Builds and spawns the sprites
74    let mut sprites = vec![];
75    for y in -half_y..half_y {
76        for x in -half_x..half_x {
77            let position = Vec2::new(x as f32, y as f32);
78            let translation = (position * tile_size).extend(rng.random::<f32>());
79            let rotation = Quat::from_rotation_z(rng.random::<f32>());
80            let scale = Vec3::splat(rng.random::<f32>() * 2.0);
81
82            sprites.push((
83                SpriteMesh {
84                    image: sprite_handle.clone(),
85                    custom_size: Some(tile_size),
86                    color: if color_tint.0 {
87                        COLORS[rng.random_range(0..3)]
88                    } else {
89                        Color::WHITE
90                    },
91                    ..default()
92                },
93                Transform {
94                    translation,
95                    rotation,
96                    scale,
97                },
98            ));
99        }
100    }
101    commands.spawn_batch(sprites);
102}
103
104// System for rotating and translating the camera
105fn move_camera(time: Res<Time>, mut camera_transform: Single<&mut Transform, With<Camera>>) {
106    camera_transform.rotate_z(time.delta_secs() * 0.5);
107    **camera_transform = **camera_transform
108        * Transform::from_translation(Vec3::X * CAMERA_SPEED * time.delta_secs());
109}
110
111#[derive(Deref, DerefMut)]
112struct PrintingTimer(Timer);
113
114impl Default for PrintingTimer {
115    fn default() -> Self {
116        Self(Timer::from_seconds(1.0, TimerMode::Repeating))
117    }
118}
119
120// System for printing the number of sprites on every tick of the timer
121fn print_sprite_count(
122    time: Res<Time>,
123    mut timer: Local<PrintingTimer>,
124    sprites: Query<&SpriteMesh>,
125) {
126    timer.tick(time.delta());
127
128    if timer.just_finished() {
129        info!("Sprites: {}", sprites.iter().count());
130    }
131}