bevy/errors/B0004.md
Joseph 02b520b4e8
Split ComputedVisibility into two components to allow for accurate change detection and speed up visibility propagation (#9497)
# Objective

Fix #8267.
Fixes half of #7840.

The `ComputedVisibility` component contains two flags: hierarchy
visibility, and view visibility (whether its visible to any cameras).
Due to the modular and open-ended way that view visibility is computed,
it triggers change detection every single frame, even when the value
does not change. Since hierarchy visibility is stored in the same
component as view visibility, this means that change detection for
inherited visibility is completely broken.

At the company I work for, this has become a real issue. We are using
change detection to only re-render scenes when necessary. The broken
state of change detection for computed visibility means that we have to
to rely on the non-inherited `Visibility` component for now. This is
workable in the early stages of our project, but since we will
inevitably want to use the hierarchy, we will have to either:

1. Roll our own solution for computed visibility.
2. Fix the issue for everyone.

## Solution

Split the `ComputedVisibility` component into two: `InheritedVisibilty`
and `ViewVisibility`.
This allows change detection to behave properly for
`InheritedVisibility`.
View visiblity is still erratic, although it is less useful to be able
to detect changes
for this flavor of visibility.

Overall, this actually simplifies the API. Since the visibility system
consists of
self-explaining components, it is much easier to document the behavior
and usage.
This approach is more modular and "ECS-like" -- one could
strip out the `ViewVisibility` component entirely if it's not needed,
and rely only on inherited visibility.

---

## Changelog

- `ComputedVisibility` has been removed in favor of:
`InheritedVisibility` and `ViewVisiblity`.

## Migration Guide

The `ComputedVisibilty` component has been split into
`InheritedVisiblity` and
`ViewVisibility`. Replace any usages of
`ComputedVisibility::is_visible_in_hierarchy`
with `InheritedVisibility::get`, and replace
`ComputedVisibility::is_visible_in_view`
 with `ViewVisibility::get`.
 
 ```rust
 // Before:
 commands.spawn(VisibilityBundle {
     visibility: Visibility::Inherited,
     computed_visibility: ComputedVisibility::default(),
 });
 
 // After:
 commands.spawn(VisibilityBundle {
     visibility: Visibility::Inherited,
     inherited_visibility: InheritedVisibility::default(),
     view_visibility: ViewVisibility::default(),
 });
 ```
 
 ```rust
 // Before:
 fn my_system(q: Query<&ComputedVisibilty>) {
     for vis in &q {
         if vis.is_visible_in_hierarchy() {
     
 // After:
 fn my_system(q: Query<&InheritedVisibility>) {
     for inherited_visibility in &q {
         if inherited_visibility.get() {
 ```
 
 ```rust
 // Before:
 fn my_system(q: Query<&ComputedVisibilty>) {
     for vis in &q {
         if vis.is_visible_in_view() {
     
 // After:
 fn my_system(q: Query<&ViewVisibility>) {
     for view_visibility in &q {
         if view_visibility.get() {
 ```
 
 ```rust
 // Before:
 fn my_system(mut q: Query<&mut ComputedVisibilty>) {
     for vis in &mut q {
         vis.set_visible_in_view();
     
 // After:
 fn my_system(mut q: Query<&mut ViewVisibility>) {
     for view_visibility in &mut q {
         view_visibility.set();
 ```

---------

Co-authored-by: Robert Swain <robert.swain@gmail.com>
2023-09-01 13:00:18 +00:00

120 lines
4 KiB
Markdown

# B0004
A runtime warning.
An [`Entity`] with a hierarchy-inherited component has a [`Parent`]
without the hierarchy-inherited component in question.
The hierarchy-inherited components defined in bevy include:
- [`InheritedVisibility`]
- [`GlobalTransform`]
Third party plugins may also define their own hierarchy components, so
read the warning message carefully and pay attention to the exact type
of the missing component.
To fix this warning, add the missing hierarchy component to all ancestors
of entities with the hierarchy component you wish to use.
The following code will cause a warning to be emitted:
```rust,no_run
use bevy::prelude::*;
// WARNING: this code is buggy
fn setup_cube(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
commands
.spawn(TransformBundle::default())
.with_children(|parent| {
// cube
parent.spawn(PbrBundle {
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()),
transform: Transform::from_xyz(0.0, 0.5, 0.0),
..default()
});
});
// camera
commands.spawn(Camera3dBundle {
transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
..default()
});
}
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup_cube)
.run();
}
```
This code **will not** show a cube on screen.
This is because the entity spawned with `commands.spawn(…)`
doesn't have a [`ViewVisibility`] or [`InheritedVisibility`] component.
Since the cube is spawned as a child of an entity without the
visibility components, it will not be visible at all.
To fix this, you must use [`SpatialBundle`] over [`TransformBundle`],
as follows:
```rust,no_run
use bevy::prelude::*;
fn setup_cube(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
commands
// We use SpatialBundle instead of TransformBundle, it contains the
// visibility components needed to display the cube,
// In addition to the Transform and GlobalTransform components.
.spawn(SpatialBundle::default())
.with_children(|parent| {
// cube
parent.spawn(PbrBundle {
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()),
transform: Transform::from_xyz(0.0, 0.5, 0.0),
..default()
});
});
// camera
commands.spawn(Camera3dBundle {
transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
..default()
});
}
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup_cube)
.run();
}
```
A similar problem occurs when the [`GlobalTransform`] component is missing.
However, when a parent [`GlobalTransform`] is missing,
it will simply prevent all transform propagation,
including when updating the [`Transform`] component of the child.
You will most likely encounter this warning when loading a scene
as a child of a pre-existing [`Entity`] that does not have the proper components.
[`InheritedVisibility`]: https://docs.rs/bevy/*/bevy/render/view/struct.InheritedVisibility.html
[`ViewVisibility`]: https://docs.rs/bevy/*/bevy/render/view/struct.ViewVisibility.html
[`GlobalTransform`]: https://docs.rs/bevy/*/bevy/transform/components/struct.GlobalTransform.html
[`Transform`]: https://docs.rs/bevy/*/bevy/transform/components/struct.Transform.html
[`Parent`]: https://docs.rs/bevy/*/bevy/hierarchy/struct.Parent.html
[`Entity`]: https://docs.rs/bevy/*/bevy/ecs/entity/struct.Entity.html
[`SpatialBundle`]: https://docs.rs/bevy/*/bevy/render/prelude/struct.SpatialBundle.html
[`TransformBundle`]: https://docs.rs/bevy/*/bevy/transform/struct.TransformBundle.html