154 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			154 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
//! Disabling entities is a powerful feature that allows you to hide entities from the ECS without deleting them.
 | 
						|
//!
 | 
						|
//! This can be useful for implementing features like "sleeping" objects that are offscreen
 | 
						|
//! or managing networked entities.
 | 
						|
//!
 | 
						|
//! While disabling entities *will* make them invisible,
 | 
						|
//! that's not its primary purpose!
 | 
						|
//! [`Visibility`](bevy::prelude::Visibility) should be used to hide entities;
 | 
						|
//! disabled entities are skipped entirely, which can lead to subtle bugs.
 | 
						|
//!
 | 
						|
//! # Default query filters
 | 
						|
//!
 | 
						|
//! Under the hood, Bevy uses a "default query filter" that skips entities with the
 | 
						|
//! the [`Disabled`] component.
 | 
						|
//! These filters act as a by-default exclusion list for all queries,
 | 
						|
//! and can be bypassed by explicitly including these components in your queries.
 | 
						|
//! For example, `Query<&A, With<Disabled>`, `Query<(Entity, Has<Disabled>>)` or
 | 
						|
//! `Query<&A, Or<(With<Disabled>, With<B>)>>` will include disabled entities.
 | 
						|
 | 
						|
use bevy::ecs::entity_disabling::Disabled;
 | 
						|
use bevy::prelude::*;
 | 
						|
 | 
						|
fn main() {
 | 
						|
    App::new()
 | 
						|
        .add_plugins((DefaultPlugins, MeshPickingPlugin))
 | 
						|
        .add_observer(disable_entities_on_click)
 | 
						|
        .add_systems(
 | 
						|
            Update,
 | 
						|
            (list_all_named_entities, reenable_entities_on_space),
 | 
						|
        )
 | 
						|
        .add_systems(Startup, (setup_scene, display_instructions))
 | 
						|
        .run();
 | 
						|
}
 | 
						|
 | 
						|
#[derive(Component)]
 | 
						|
struct DisableOnClick;
 | 
						|
 | 
						|
fn disable_entities_on_click(
 | 
						|
    trigger: On<Pointer<Click>>,
 | 
						|
    valid_query: Query<&DisableOnClick>,
 | 
						|
    mut commands: Commands,
 | 
						|
) {
 | 
						|
    let clicked_entity = trigger.target();
 | 
						|
    // Windows and text are entities and can be clicked!
 | 
						|
    // We definitely don't want to disable the window itself,
 | 
						|
    // because that would cause the app to close!
 | 
						|
    if valid_query.contains(clicked_entity) {
 | 
						|
        // Just add the `Disabled` component to the entity to disable it.
 | 
						|
        // Note that the `Disabled` component is *only* added to the entity,
 | 
						|
        // its children are not affected.
 | 
						|
        commands.entity(clicked_entity).insert(Disabled);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
#[derive(Component)]
 | 
						|
struct EntityNameText;
 | 
						|
 | 
						|
// The query here will not find entities with the `Disabled` component,
 | 
						|
// because it does not explicitly include it.
 | 
						|
fn list_all_named_entities(
 | 
						|
    query: Query<&Name>,
 | 
						|
    mut name_text_query: Query<&mut Text, With<EntityNameText>>,
 | 
						|
    mut commands: Commands,
 | 
						|
) {
 | 
						|
    let mut text_string = String::from("Named entities found:\n");
 | 
						|
    // Query iteration order is not guaranteed, so we sort the names
 | 
						|
    // to ensure the output is consistent.
 | 
						|
    for name in query.iter().sort::<&Name>() {
 | 
						|
        text_string.push_str(&format!("{name:?}\n"));
 | 
						|
    }
 | 
						|
 | 
						|
    if let Ok(mut text) = name_text_query.single_mut() {
 | 
						|
        *text = Text::new(text_string);
 | 
						|
    } else {
 | 
						|
        commands.spawn((
 | 
						|
            EntityNameText,
 | 
						|
            Text::default(),
 | 
						|
            Node {
 | 
						|
                position_type: PositionType::Absolute,
 | 
						|
                top: Val::Px(12.0),
 | 
						|
                right: Val::Px(12.0),
 | 
						|
                ..default()
 | 
						|
            },
 | 
						|
        ));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
fn reenable_entities_on_space(
 | 
						|
    mut commands: Commands,
 | 
						|
    // This query can find disabled entities,
 | 
						|
    // because it explicitly includes the `Disabled` component.
 | 
						|
    disabled_entities: Query<Entity, With<Disabled>>,
 | 
						|
    input: Res<ButtonInput<KeyCode>>,
 | 
						|
) {
 | 
						|
    if input.just_pressed(KeyCode::Space) {
 | 
						|
        for entity in disabled_entities.iter() {
 | 
						|
            // To re-enable an entity, just remove the `Disabled` component.
 | 
						|
            commands.entity(entity).remove::<Disabled>();
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
const X_EXTENT: f32 = 900.;
 | 
						|
 | 
						|
fn setup_scene(
 | 
						|
    mut commands: Commands,
 | 
						|
    mut meshes: ResMut<Assets<Mesh>>,
 | 
						|
    mut materials: ResMut<Assets<ColorMaterial>>,
 | 
						|
) {
 | 
						|
    commands.spawn(Camera2d);
 | 
						|
 | 
						|
    let named_shapes = [
 | 
						|
        (Name::new("Annulus"), meshes.add(Annulus::new(25.0, 50.0))),
 | 
						|
        (
 | 
						|
            Name::new("Bestagon"),
 | 
						|
            meshes.add(RegularPolygon::new(50.0, 6)),
 | 
						|
        ),
 | 
						|
        (Name::new("Rhombus"), meshes.add(Rhombus::new(75.0, 100.0))),
 | 
						|
    ];
 | 
						|
    let num_shapes = named_shapes.len();
 | 
						|
 | 
						|
    for (i, (name, shape)) in named_shapes.into_iter().enumerate() {
 | 
						|
        // Distribute colors evenly across the rainbow.
 | 
						|
        let color = Color::hsl(360. * i as f32 / num_shapes as f32, 0.95, 0.7);
 | 
						|
 | 
						|
        commands.spawn((
 | 
						|
            name,
 | 
						|
            DisableOnClick,
 | 
						|
            Mesh2d(shape),
 | 
						|
            MeshMaterial2d(materials.add(color)),
 | 
						|
            Transform::from_xyz(
 | 
						|
                // Distribute shapes from -X_EXTENT/2 to +X_EXTENT/2.
 | 
						|
                -X_EXTENT / 2. + i as f32 / (num_shapes - 1) as f32 * X_EXTENT,
 | 
						|
                0.0,
 | 
						|
                0.0,
 | 
						|
            ),
 | 
						|
        ));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
fn display_instructions(mut commands: Commands) {
 | 
						|
    commands.spawn((
 | 
						|
        Text::new(
 | 
						|
            "Click an entity to disable it.\n\nPress Space to re-enable all disabled entities.",
 | 
						|
        ),
 | 
						|
        Node {
 | 
						|
            position_type: PositionType::Absolute,
 | 
						|
            top: Val::Px(12.0),
 | 
						|
            left: Val::Px(12.0),
 | 
						|
            ..default()
 | 
						|
        },
 | 
						|
    ));
 | 
						|
}
 |