Visibility

Relevant official examples: parenting.


Visibility is used to control if something is to be rendered or not. If you want an entity to exist in the world, just not be displayed, you can hide it.

/// Prepare the game map, but do not display it until later
fn setup_map_hidden(
    mut commands: Commands,
) {
    commands.spawn((
        GameMapEntity,
        SceneBundle {
            scene: todo!(),
            visibility: Visibility {
                is_visible: false,
            },
            ..Default::default()
        },
    ));
}

/// When everything is ready, un-hide the game map
fn reveal_map(
    mut query: Query<&mut Visibility, With<GameMapEntity>>,
) {
    let mut vis_map = query.single_mut();
    vis_map.is_visible = true;
}

Visibility Components

In Bevy, visibility is represented by two components: Visibility and ComputedVisibility.

Any Entity that represents an object in the game world needs to have both. All of Bevy's built-in bundle types include them.

If you are creating a custom entity without using those bundles, you can use one of the following to ensure you don't miss them:

fn spawn_special_entity(
    mut commands: Commands,
) {
    // create an entity that does not use one of the common Bevy bundles,
    // but still needs transforms and visibility
    commands.spawn((
        ComponentA,
        ComponentB,
        SpatialBundle {
            transform: Transform::from_scale(Vec3::splat(3.0)),
            visibility: Visibility {
                is_visible: false,
            },
            ..Default::default()
        },
    ));
}

Visibility

Visibility is the "user-facing toggle". This is where you specify if you want this entity to be visible or not.

If you hide an entity that has children, they will also be hidden, regardless of what their individual Visibility value is set to.

ComputedVisibility

ComputedVisibility represents the actual final decision made by Bevy about whether this entity needs to be displayed.

The value of ComputedVisibility is read-only. It is managed internally by Bevy.

It can be affected by the visibility of parent entities, if any.

It is also affected by "culling" systems. If the entity is not in the range of any Camera or Light, it does not need to be rendered, so Bevy will hide it.

Checking Actual Visibility

You can use ComputedVisibility to check if the entity is actually visible.

Bevy's internal visibility computations are done in the PostUpdate stage. To get the up-to-date values for the current frame, your system must be ordered after these bevy-internal systems. You can use the VisibilitySystems labels.

/// Check if the Player was hidden manually
fn debug_player_visibility(
    query: Query<&ComputedVisibility, With<Player>>,
) {
    let vis = query.single();

    // check if it was manually hidden via Visibility
    // (incl. by any parent entity)
    debug!("Player visible due to hierachy: {:?}", vis.is_visible_in_hierarchy());
}

/// Check if balloons are seen by any Camera, Light, etc… (not culled)
fn debug_balloon_visibility(
    query: Query<&ComputedVisibility, With<Balloon>>,
) {
    for vis in query.iter() {
        debug!("Balloon is in view: {:?}", vis.is_visible_in_view());

        // check overall final actual visibility
        // (combines visible-in-hierarchy and visible-in-view)
        debug!("Balloon is visible: {:?}", vis.is_visible());
    }
}

fn main() {
    // the labels to use for ordering
    use bevy::render::view::VisibilitySystems;

    App::new()
        .add_plugins(DefaultPlugins)
        .add_system_to_stage(
            CoreStage::PostUpdate,
            debug_balloon_visibility
                // in-view visibility (culling) is done here:
                .after(VisibilitySystems::CheckVisibility)
        )
        .add_system_to_stage(
            CoreStage::PostUpdate,
            debug_player_visibility
                // hierarchy propagation is done here:
                .after(VisibilitySystems::VisibilityPropagate)
        )
        .run();
}