|
| 1 | +//! Renders a lot of animated sprites to allow performance testing. |
| 2 | +//! |
| 3 | +//! It sets up many animated sprites in different sizes and rotations, and at different scales in the world, |
| 4 | +//! and moves the camera over them to see how well frustum culling works. |
| 5 | +//! |
| 6 | +//! To measure performance realistically, be sure to run this in release mode. |
| 7 | +//! `cargo run --example many_animated_sprites --release` |
| 8 | +
|
| 9 | +use std::time::Duration; |
| 10 | + |
| 11 | +use bevy::{ |
| 12 | + diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, |
| 13 | + math::Quat, |
| 14 | + prelude::*, |
| 15 | + render::camera::Camera, |
| 16 | +}; |
| 17 | + |
| 18 | +use rand::Rng; |
| 19 | + |
| 20 | +const CAMERA_SPEED: f32 = 1000.0; |
| 21 | + |
| 22 | +fn main() { |
| 23 | + App::new() |
| 24 | + // Since this is also used as a benchmark, we want it to display performance data. |
| 25 | + .add_plugin(LogDiagnosticsPlugin::default()) |
| 26 | + .add_plugin(FrameTimeDiagnosticsPlugin::default()) |
| 27 | + .add_plugins(DefaultPlugins) |
| 28 | + .add_startup_system(setup) |
| 29 | + .add_system(animate_sprite) |
| 30 | + .add_system(print_sprite_count) |
| 31 | + .add_system(move_camera.after(print_sprite_count)) |
| 32 | + .run(); |
| 33 | +} |
| 34 | + |
| 35 | +fn setup( |
| 36 | + mut commands: Commands, |
| 37 | + assets: Res<AssetServer>, |
| 38 | + mut texture_atlases: ResMut<Assets<TextureAtlas>>, |
| 39 | +) { |
| 40 | + let mut rng = rand::thread_rng(); |
| 41 | + |
| 42 | + let tile_size = Vec2::splat(64.0); |
| 43 | + let map_size = Vec2::splat(320.0); |
| 44 | + |
| 45 | + let half_x = (map_size.x / 2.0) as i32; |
| 46 | + let half_y = (map_size.y / 2.0) as i32; |
| 47 | + |
| 48 | + let texture_handle = assets.load("textures/rpg/chars/gabe/gabe-idle-run.png"); |
| 49 | + let texture_atlas = TextureAtlas::from_grid(texture_handle, Vec2::new(24.0, 24.0), 7, 1); |
| 50 | + let texture_atlas_handle = texture_atlases.add(texture_atlas); |
| 51 | + |
| 52 | + // Spawns the camera |
| 53 | + commands |
| 54 | + .spawn() |
| 55 | + .insert_bundle(Camera2dBundle::default()) |
| 56 | + .insert(Transform::from_xyz(0.0, 0.0, 1000.0)); |
| 57 | + |
| 58 | + // Builds and spawns the sprites |
| 59 | + for y in -half_y..half_y { |
| 60 | + for x in -half_x..half_x { |
| 61 | + let position = Vec2::new(x as f32, y as f32); |
| 62 | + let translation = (position * tile_size).extend(rng.gen::<f32>()); |
| 63 | + let rotation = Quat::from_rotation_z(rng.gen::<f32>()); |
| 64 | + let scale = Vec3::splat(rng.gen::<f32>() * 2.0); |
| 65 | + let mut timer = Timer::from_seconds(0.1, true); |
| 66 | + timer.set_elapsed(Duration::from_secs_f32(rng.gen::<f32>())); |
| 67 | + |
| 68 | + commands |
| 69 | + .spawn_bundle(SpriteSheetBundle { |
| 70 | + texture_atlas: texture_atlas_handle.clone(), |
| 71 | + transform: Transform { |
| 72 | + translation, |
| 73 | + rotation, |
| 74 | + scale, |
| 75 | + }, |
| 76 | + sprite: TextureAtlasSprite { |
| 77 | + custom_size: Some(tile_size), |
| 78 | + ..default() |
| 79 | + }, |
| 80 | + ..default() |
| 81 | + }) |
| 82 | + .insert(AnimationTimer(timer)); |
| 83 | + } |
| 84 | + } |
| 85 | +} |
| 86 | + |
| 87 | +// System for rotating and translating the camera |
| 88 | +fn move_camera(time: Res<Time>, mut camera_query: Query<&mut Transform, With<Camera>>) { |
| 89 | + let mut camera_transform = camera_query.single_mut(); |
| 90 | + camera_transform.rotate(Quat::from_rotation_z(time.delta_seconds() * 0.5)); |
| 91 | + *camera_transform = *camera_transform |
| 92 | + * Transform::from_translation(Vec3::X * CAMERA_SPEED * time.delta_seconds()); |
| 93 | +} |
| 94 | + |
| 95 | +#[derive(Component, Deref, DerefMut)] |
| 96 | +struct AnimationTimer(Timer); |
| 97 | + |
| 98 | +fn animate_sprite( |
| 99 | + time: Res<Time>, |
| 100 | + texture_atlases: Res<Assets<TextureAtlas>>, |
| 101 | + mut query: Query<( |
| 102 | + &mut AnimationTimer, |
| 103 | + &mut TextureAtlasSprite, |
| 104 | + &Handle<TextureAtlas>, |
| 105 | + )>, |
| 106 | +) { |
| 107 | + for (mut timer, mut sprite, texture_atlas_handle) in query.iter_mut() { |
| 108 | + timer.tick(time.delta()); |
| 109 | + if timer.just_finished() { |
| 110 | + let texture_atlas = texture_atlases.get(texture_atlas_handle).unwrap(); |
| 111 | + sprite.index = (sprite.index + 1) % texture_atlas.textures.len(); |
| 112 | + } |
| 113 | + } |
| 114 | +} |
| 115 | + |
| 116 | +#[derive(Deref, DerefMut)] |
| 117 | +struct PrintingTimer(Timer); |
| 118 | + |
| 119 | +impl Default for PrintingTimer { |
| 120 | + fn default() -> Self { |
| 121 | + Self(Timer::from_seconds(1.0, true)) |
| 122 | + } |
| 123 | +} |
| 124 | + |
| 125 | +// System for printing the number of sprites on every tick of the timer |
| 126 | +fn print_sprite_count( |
| 127 | + time: Res<Time>, |
| 128 | + mut timer: Local<PrintingTimer>, |
| 129 | + sprites: Query<&TextureAtlasSprite>, |
| 130 | +) { |
| 131 | + timer.tick(time.delta()); |
| 132 | + |
| 133 | + if timer.just_finished() { |
| 134 | + info!("Sprites: {}", sprites.iter().count(),); |
| 135 | + } |
| 136 | +} |
0 commit comments