use bevy::{color::palettes::tailwind, prelude::*, window::WindowResolution};
use bevy_uniform_grid_2d::prelude::*;
use iyes_perf_ui::{
entries::{
PerfUiFixedTimeEntries, PerfUiFramerateEntries, PerfUiSystemEntries, PerfUiWindowEntries,
},
prelude::*,
};
use rand::Rng;
const ON: Color = Color::Srgba(tailwind::GRAY_200);
const OFF: Color = Color::Srgba(tailwind::RED_500);
const OUT: Color = Color::Srgba(tailwind::GRAY_950);
const N: usize = 8;
fn main() {
let mut app = App::new();
app.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
resolution: WindowResolution::new(800., 800.),
title: "Stress Test Example".to_string(),
present_mode: bevy::window::PresentMode::Immediate, ..default()
}),
..default()
}))
.add_plugins(bevy::diagnostic::FrameTimeDiagnosticsPlugin::default())
.add_plugins(PerfUiPlugin)
.add_plugins(
UniformGrid2dPlugin::<Marker, N>::default()
.debug(true)
.dimensions(UVec2::splat(30))
.spacing(Vec2::splat(20.)),
)
.insert_resource(ChangeDirectionTimer(Timer::from_seconds(
3.,
TimerMode::Repeating,
)))
.add_systems(Startup, setup)
.add_systems(Update, movement)
.add_systems(Update, update_color)
.run();
}
#[derive(Resource)]
struct ChangeDirectionTimer(Timer);
#[derive(Component)]
struct Direction(Vec2);
#[derive(Component)]
struct Marker;
fn setup(mut commands: Commands, grid: Res<Grid<Marker, N>>) {
commands.spawn((
PerfUiRoot::default(),
PerfUiFramerateEntries::default(),
PerfUiWindowEntries::default(),
PerfUiSystemEntries::default(),
PerfUiFixedTimeEntries::default(),
));
let mut rng = rand::thread_rng();
let padding = 50.;
let max = grid.dimensions().as_vec2() * grid.spacing() + Vec2::splat(padding) + grid.anchor();
let min = Vec2::splat(-padding) + grid.anchor();
let entity_count = 1000;
let entity_size = Vec2::splat(5.);
for _ in 0..entity_count {
let position = Vec2::new(rng.gen_range(min.x..max.x), rng.gen_range(min.y..max.y));
let direction = Vec2::new(rng.gen_range(-1.0..=1.0), rng.gen_range(-1.0..=1.0)).normalize();
commands.spawn((
Sprite {
color: OUT,
custom_size: Some(entity_size),
..default()
},
Transform::from_xyz(position.x, position.y, 10.),
Direction(direction),
Marker,
));
}
commands.spawn((Camera2d, Transform::from_xyz(max.x / 2., max.y / 2., 0.)));
}
fn movement(
time: Res<Time>,
mut direction_timer: ResMut<ChangeDirectionTimer>,
mut query: Query<(&mut Transform, &mut Direction)>,
) {
let mut rng = rand::thread_rng();
let t = time.delta_secs();
direction_timer.0.tick(time.delta());
let change_direction = direction_timer.0.just_finished();
for (mut transform, mut direction) in &mut query {
if change_direction {
*direction = Direction(
Vec2::new(rng.gen_range(-1.0..=1.0), rng.gen_range(-1.0..=1.0)).normalize(),
);
}
transform.translation += t * 10. * direction.0.extend(0.);
}
}
fn update_color(mut sprites: Query<&mut Sprite>, mut events: EventReader<GridEvent>) {
for &GridEvent { entity, operation } in events.read() {
let Ok(mut sprite) = sprites.get_mut(entity) else {
continue;
};
match operation {
GridOperation::Update { .. } => {
if sprite.color == ON {
sprite.color = OFF;
} else {
sprite.color = ON;
}
}
GridOperation::Insert { .. } => {
let mut rng = rand::thread_rng();
sprite.color = if rng.gen_bool(0.5) { OFF } else { ON };
}
GridOperation::Remove { .. } => {
sprite.color = OUT;
}
}
}
}