Design patterns
Log let else queries
The typical quick way when prototyping queries with only one expected item is to just use unwrap
However a slighter better option is to use let else
statements.
lets start with an example
based on our code here.
use std::str::FromStr;
use bevy::{log::LogPlugin, prelude::*};
use rand::prelude::*;
fn main() {
App::new()
//Disable the defaults
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(Startup, spammy::print_trash)
.add_systems(Update, some_queries.run_if(run_once()))
.add_systems(Update, query_light.run_if(run_once()))
.run();
}
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
// Camera
commands.spawn(Camera3dBundle {
transform: Transform::from_xyz(0.0, 50.0, 100.0).looking_at(Vec3::ZERO, Vec3::Y),
..default()
});
// Light
// commands.spawn(PointLightBundle {
// point_light: PointLight {
// intensity: 1500.0,
// shadows_enabled: true,
// ..default()
// },
// transform: Transform::from_xyz(4.0, 8.0, 4.0),
// ..default()
// });
let mut rng = rand::thread_rng();
// Create spiral galaxy
for i in 0..10000 {
let t = i as f32 * 0.005;
let r = 20.0 * t.sqrt();
let theta = t * 2.5;
let x = r * theta.cos();
let z = r * theta.sin();
let y = (rng.gen::<f32>() - 0.5) * 2.0; // Add some vertical spread
let size = (0.05 + rng.gen::<f32>() * 0.1) * (1.0 - t / 50.0); // Smaller stars towards the edge
let brightness = 1.0 - t / 50.0; // Dimmer stars towards the edge
let color = Color::srgb(brightness, brightness * 0.8 + 0.2, brightness * 0.6 + 0.4);
let emissive = Color::srgb(
brightness * 5.0,
(brightness * 0.8 + 0.2) * 5.0,
(brightness * 0.6 + 0.4) * 5.0,
);
commands.spawn(PbrBundle {
mesh: meshes.add(Mesh::from(Sphere { radius: size })),
material: materials.add(StandardMaterial {
base_color: color,
emissive: emissive.into(), // Make stars glow
..default()
}),
transform: Transform::from_xyz(x, y, z),
..default()
});
}
}
fn some_queries(transformers: Query<&Transform>) {}
fn query_light(light_query: Query<&PointLight>) {
let light = light_query.get_single().unwrap();
info!("found the light");
}
mod spammy {
pub fn print_trash() {
for i in 0..10 {
some_function(i);
}
}
fn some_function(i: u8) {
if i == 3 {}
}
}
A query has been added for the pointlight but it no longer is created we can see what happens
Running `target/debug/test_spiral`
2024-10-20T23:47:00.160263Z INFO bevy_diagnostic::system_information_diagnostics_plugin::internal: SystemInfo { os: "Linux 22.04 KDE neon", kernel: "6.8.0-45-generic", cpu: "AMD Ryzen 7 7800X3D 8-Core Processor", core_count: "8", memory: "30.5 GiB" }
2024-10-20T23:47:00.274629Z INFO bevy_render::renderer: AdapterInfo { name: "NVIDIA GeForce RTX 3070", vendor: 4318, device: 9348, device_type: DiscreteGpu, driver: "NVIDIA", driver_info: "535.183.01", backend: Vulkan }
2024-10-20T23:47:00.691667Z INFO bevy_winit::system: Creating new window "App" (Entity { index: 0, generation: 1 })
2024-10-20T23:47:00.692031Z INFO winit::platform_impl::linux::x11::window: Guessed window scale factor: 1
thread 'Compute Task Pool (3)' panicked at src/main.rs:77:42:
called `Result::unwrap()` on an `Err` value: NoEntities("bevy_ecs::query::state::QueryState<&bevy_pbr::light::point_light::PointLight>")
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Encountered a panic in system `test_spiral::query_light`!
Encountered a panic in system `bevy_app::main_schedule::Main::run_main`!
A crash its good we got the issue pointed out to us straight away but we could change the query_light function to be as below.
fn query_light(light_query: Query<&PointLight>) {
let light = light_query.get_single() else {
error!("no light found");
return;
};
info!("found the light");
}
By stopping crashing but logging the issues we can keep the game going and possibly find multiple issues in one run.
Plus if the system is not that vital, we don't need to ruin the user's experience.
Running `target/debug/test_spiral`
2024-10-20T23:53:21.775972Z INFO bevy_diagnostic::system_information_diagnostics_plugin::internal: SystemInfo { os: "Linux 22.04 KDE neon", kernel: "6.8.0-45-generic", cpu: "AMD Ryzen 7 7800X3D 8-Core Processor", core_count: "8", memory: "30.5 GiB" }
2024-10-20T23:53:21.904099Z INFO bevy_render::renderer: AdapterInfo { name: "NVIDIA GeForce RTX 3070", vendor: 4318, device: 9348, device_type: DiscreteGpu, driver: "NVIDIA", driver_info: "535.183.01", backend: Vulkan }
2024-10-20T23:53:22.316914Z INFO bevy_winit::system: Creating new window "App" (Entity { index: 0, generation: 1 })
2024-10-20T23:53:22.317300Z INFO winit::platform_impl::linux::x11::window: Guessed window scale factor: 1
2024-10-20T23:53:23.645988Z INFO test_spiral: found the light
Prefer to use continue
over return in loops otherwise one bad apple will destroy bunch