//! This example demonstrates how fallible parameters can prevent their systems //! from running if their acquiry conditions aren't met. //! //! Fallible system parameters include: //! - [`Res`], [`ResMut`] - Resource has to exist, and the [`GLOBAL_ERROR_HANDLER`] will be called if it doesn't. //! - [`Single`] - There must be exactly one matching entity, but the system will be silently skipped otherwise. //! - [`Option>`] - There must be zero or one matching entity. The system will be silently skipped if there are more. //! - [`Populated`] - There must be at least one matching entity, but the system will be silently skipped otherwise. //! //! Other system parameters, such as [`Query`], will never fail validation: returning a query with no matching entities is valid. //! //! The result of failed system parameter validation is determined by the [`SystemParamValidationError`] returned //! by [`SystemParam::validate_param`] for each system parameter. //! Each system will pass if all of its parameters are valid, or else return [`SystemParamValidationError`] for the first failing parameter. //! //! To learn more about setting the fallback behavior for [`SystemParamValidationError`] failures, //! please see the `error_handling.rs` example. //! //! [`SystemParamValidationError`]: bevy::ecs::system::SystemParamValidationError //! [`SystemParam::validate_param`]: bevy::ecs::system::SystemParam::validate_param use bevy::ecs::error::{warn, GLOBAL_ERROR_HANDLER}; use bevy::prelude::*; use rand::Rng; fn main() { // By default, if a parameter fail to be fetched, // the `GLOBAL_ERROR_HANDLER` will be used to handle the error, // which by default is set to panic. GLOBAL_ERROR_HANDLER .set(warn) .expect("The error handler can only be set once, globally."); println!(); println!("Press 'A' to add enemy ships and 'R' to remove them."); println!("Player ship will wait for enemy ships and track one if it exists,"); println!("but will stop tracking if there are more than one."); println!(); App::new() .add_plugins(DefaultPlugins) .add_systems(Startup, setup) .add_systems(Update, (user_input, move_targets, track_targets).chain()) // This system will always fail validation, because we never create an entity with both `Player` and `Enemy` components. .add_systems(Update, do_nothing_fail_validation) .run(); } /// Enemy component stores data for movement in a circle. #[derive(Component, Default)] struct Enemy { origin: Vec2, radius: f32, rotation: f32, rotation_speed: f32, } /// Player component stores data for going after enemies. #[derive(Component, Default)] struct Player { speed: f32, rotation_speed: f32, min_follow_radius: f32, } fn setup(mut commands: Commands, asset_server: Res) { // Spawn 2D camera. commands.spawn(Camera2d); // Spawn player. let texture = asset_server.load("textures/simplespace/ship_C.png"); commands.spawn(( Player { speed: 100.0, rotation_speed: 2.0, min_follow_radius: 50.0, }, Sprite { image: texture, color: bevy::color::palettes::tailwind::BLUE_800.into(), ..Default::default() }, Transform::from_translation(Vec3::ZERO), )); } /// System that reads user input. /// If user presses 'A' we spawn a new random enemy. /// If user presses 'R' we remove a random enemy (if any exist). fn user_input( mut commands: Commands, enemies: Query>, keyboard_input: Res>, asset_server: Res, ) { let mut rng = rand::thread_rng(); if keyboard_input.just_pressed(KeyCode::KeyA) { let texture = asset_server.load("textures/simplespace/enemy_A.png"); commands.spawn(( Enemy { origin: Vec2::new(rng.gen_range(-200.0..200.0), rng.gen_range(-200.0..200.0)), radius: rng.gen_range(50.0..150.0), rotation: rng.gen_range(0.0..std::f32::consts::TAU), rotation_speed: rng.gen_range(0.5..1.5), }, Sprite { image: texture, color: bevy::color::palettes::tailwind::RED_800.into(), ..default() }, Transform::from_translation(Vec3::ZERO), )); } if keyboard_input.just_pressed(KeyCode::KeyR) { if let Some(entity) = enemies.iter().next() { commands.entity(entity).despawn(); } } } // System that moves the enemies in a circle. // Only runs if there are enemies, due to the `Populated` parameter. fn move_targets(mut enemies: Populated<(&mut Transform, &mut Enemy)>, time: Res