//! This example demonstrates how fallible parameters can prevent their systems1//! from running if their acquiry conditions aren't met.2//!3//! Fallible system parameters include:4//! - [`Res<R>`], [`ResMut<R>`] - Resource has to exist, and the [`World::default_error_handler`] will be called if it doesn't.5//! - [`Single<D, F>`] - There must be exactly one matching entity, but the system will be silently skipped otherwise.6//! - [`Option<Single<D, F>>`] - There must be zero or one matching entity. The system will be silently skipped if there are more.7//! - [`Populated<D, F>`] - There must be at least one matching entity, but the system will be silently skipped otherwise.8//!9//! Other system parameters, such as [`Query`], will never fail validation: returning a query with no matching entities is valid.10//!11//! The result of failed system parameter validation is determined by the [`SystemParamValidationError`] returned12//! by [`SystemParam::validate_param`] for each system parameter.13//! Each system will pass if all of its parameters are valid, or else return [`SystemParamValidationError`] for the first failing parameter.14//!15//! To learn more about setting the fallback behavior for [`SystemParamValidationError`] failures,16//! please see the `error_handling.rs` example.17//!18//! [`SystemParamValidationError`]: bevy::ecs::system::SystemParamValidationError19//! [`SystemParam::validate_param`]: bevy::ecs::system::SystemParam::validate_param2021use bevy::ecs::error::warn;22use bevy::prelude::*;23use rand::Rng;2425fn main() {26println!();27println!("Press 'A' to add enemy ships and 'R' to remove them.");28println!("Player ship will wait for enemy ships and track one if it exists,");29println!("but will stop tracking if there are more than one.");30println!();3132App::new()33// By default, if a parameter fail to be fetched,34// `World::get_default_error_handler` will be used to handle the error,35// which by default is set to panic.36.set_error_handler(warn)37.add_plugins(DefaultPlugins)38.add_systems(Startup, setup)39.add_systems(Update, (user_input, move_targets, track_targets).chain())40// This system will always fail validation, because we never create an entity with both `Player` and `Enemy` components.41.add_systems(Update, do_nothing_fail_validation)42.run();43}4445/// Enemy component stores data for movement in a circle.46#[derive(Component, Default)]47struct Enemy {48origin: Vec2,49radius: f32,50rotation: f32,51rotation_speed: f32,52}5354/// Player component stores data for going after enemies.55#[derive(Component, Default)]56struct Player {57speed: f32,58rotation_speed: f32,59min_follow_radius: f32,60}6162fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {63// Spawn 2D camera.64commands.spawn(Camera2d);6566// Spawn player.67let texture = asset_server.load("textures/simplespace/ship_C.png");68commands.spawn((69Player {70speed: 100.0,71rotation_speed: 2.0,72min_follow_radius: 50.0,73},74Sprite {75image: texture,76color: bevy::color::palettes::tailwind::BLUE_800.into(),77..Default::default()78},79Transform::from_translation(Vec3::ZERO),80));81}8283/// System that reads user input.84/// If user presses 'A' we spawn a new random enemy.85/// If user presses 'R' we remove a random enemy (if any exist).86fn user_input(87mut commands: Commands,88enemies: Query<Entity, With<Enemy>>,89keyboard_input: Res<ButtonInput<KeyCode>>,90asset_server: Res<AssetServer>,91) {92let mut rng = rand::rng();93if keyboard_input.just_pressed(KeyCode::KeyA) {94let texture = asset_server.load("textures/simplespace/enemy_A.png");95commands.spawn((96Enemy {97origin: Vec2::new(98rng.random_range(-200.0..200.0),99rng.random_range(-200.0..200.0),100),101radius: rng.random_range(50.0..150.0),102rotation: rng.random_range(0.0..std::f32::consts::TAU),103rotation_speed: rng.random_range(0.5..1.5),104},105Sprite {106image: texture,107color: bevy::color::palettes::tailwind::RED_800.into(),108..default()109},110Transform::from_translation(Vec3::ZERO),111));112}113114if keyboard_input.just_pressed(KeyCode::KeyR)115&& let Some(entity) = enemies.iter().next()116{117commands.entity(entity).despawn();118}119}120121// System that moves the enemies in a circle.122// Only runs if there are enemies, due to the `Populated` parameter.123fn move_targets(mut enemies: Populated<(&mut Transform, &mut Enemy)>, time: Res<Time>) {124for (mut transform, mut target) in &mut *enemies {125target.rotation += target.rotation_speed * time.delta_secs();126transform.rotation = Quat::from_rotation_z(target.rotation);127let offset = transform.right() * target.radius;128transform.translation = target.origin.extend(0.0) + offset;129}130}131132/// System that moves the player, causing them to track a single enemy.133/// If there is exactly one, player will track it.134/// Otherwise, the player will search for enemies.135fn track_targets(136// `Single` ensures the system runs ONLY when exactly one matching entity exists.137mut player: Single<(&mut Transform, &Player)>,138// `Option<Single>` never prevents the system from running, but will be `None` if there is not exactly one matching entity.139enemy: Option<Single<&Transform, (With<Enemy>, Without<Player>)>>,140time: Res<Time>,141) {142let (player_transform, player) = &mut *player;143if let Some(enemy_transform) = enemy {144// Enemy found, rotate and move towards it.145let delta = enemy_transform.translation - player_transform.translation;146let distance = delta.length();147let front = delta / distance;148let up = Vec3::Z;149let side = front.cross(up);150player_transform.rotation = Quat::from_mat3(&Mat3::from_cols(side, front, up));151let max_step = distance - player.min_follow_radius;152if 0.0 < max_step {153let velocity = (player.speed * time.delta_secs()).min(max_step);154player_transform.translation += front * velocity;155}156} else {157// 0 or multiple enemies found, keep searching.158player_transform.rotate_axis(Dir3::Z, player.rotation_speed * time.delta_secs());159}160}161162/// This system always fails param validation, because we never163/// create an entity with both [`Player`] and [`Enemy`] components.164fn do_nothing_fail_validation(_: Single<(), (With<Player>, With<Enemy>)>) {}165166167