# Objective
There is a lot of `world.entities().len()`, especially in tests. In
tests, usually, the assumption is made that empty worlds do not contain
any entities. This is about to change (#19711), and as such all of these
tests are failing for that PR.
## Solution
`num_entities` is a convenience method that returns the number of
entities inside a world. It can later be adapted to exclude 'unexpected'
entities, associated with internal data structures such as Resources,
Queries, Systems. In general I argue for a separation of concepts where
`World` ignores internal entities in methods such as `iter_entities()`
and `clear_entities()`, that discussion is, however, separate from this
PR.
## Testing
I replaced most occurrences of `world.entities().len()` with
`world.num_entities()` and the tests passed.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
Some methods and commands carelessly overwrite `Relationship`
components. This may overwrite additional data stored at them which is
undesired.
Part of #19589
## Solution
A new private method will be used instead of insert:
`modify_or_insert_relation_with_relationship_hook_mode`.
This method behaves different to `insert` if `Relationship` is a larger
type than `Entity` and already contains this component. It will then use
the `modify_component` API and a new `Relationship::set_risky` method to
set the related entity, keeping all other data untouched.
For the `replace_related`(`_with_difference`) methods this also required
a `InsertHookMode` parameter for efficient modifications of multiple
children. The changes here are limited to the non-public methods.
I would appreciate feedback if this is all good.
# Testing
Added tests of all methods that previously could reset `Relationship`
data.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
I've noticed that some methods with `MaybeLocation::caller` don't have
`#[track_caller]` which resulted in wrong locations reported when
`track_location` is enabled.
## Solution
add `#[track_caller]` to them.
Click to focus is now a global observer.
# Objective
Previously, the "click to focus" behavior was implemented in each
individual headless widget, producing redundant logic.
## Solution
The new scheme is to have a global observer which looks for pointer down
events and triggers an `AcquireFocus` event on the target. This event
bubbles until it finds an entity with `TabIndex`, and then focuses it.
## Testing
Tested the changes using the various examples that have focusable
widgets. (This will become easier to test when I add focus ring support
to the examples, but that's for another day. For now you just have to
know which keys to press.)
## Migration
This change is backwards-compatible. People who want the new behavior
will need to install the new plugin.
# Objective
- Fix#19759
- The bigger permutation table only comes into play for higher
dimensions than 1, when you start doing
`PERMUTATION_TABLE[PERMUTATION_TABLE[index] + some_number]`
- The bigger permutation table has no mathematical meaning, it's just
there to avoid having to write more `& 0xFF` when doing multiple nested
lookups in higher dimensions
- But we only use 1D Perlin noise for the camera shake because we want
the dimensions to be uncorrelated
## Solution
- So, we can trim the permutation table down :)
- This should be mathematically identical, as a wrapped value will still
access the same element as an unwrapped value would in the bigger table
- The comment was a bit misleading anyways. "mirror" did not refer to
"mirrored values" but to "repeated values".
## Testing
- Ran the example. Still behaves like before.
# Objective
Add support for interpolation in OKLab and OKLCH color spaces for UI
gradients.
## Solution
* New `InterpolationColorSpace` enum with `OkLab`, `OkLch`, `OkLchLong`,
`Srgb` and `LinearRgb` variants.
* Added a color space specialization to the gradients pipeline.
* Added support for interpolation in OkLCH and OkLAB color spaces to the
gradients shader. OKLCH interpolation supports both short and long hue
paths. This is mostly based on the conversion functions from
`bevy_color` except that interpolation in polar space uses radians.
* Added `color_space` fields to each gradient type.
## Testing
The `gradients` example has been updated to demonstrate the different
color interpolation methods.
Press space to cycle through the different options.
---
## Showcase

Without this dependency, the bevy_ecs tests fail with missing as_string
methods.
# Objective
- Fixes#19734
## Solution
- add bevy_utils with feature = "Debug" to dev-dependencies
## Testing
- Ran `cargo test -p bevy_ecs`
- Ran `taplo fmt --check`
---
# Objective
There were 2 folders inside of `examples`, each with 1 example, and with
similar folder names.
## Solution
Move the example in the `usages` folder to `usage`.
## Testing
`cargo run -p ci`
# Objective
- A nightly only lint is allowed in cargo.toml, making all stable builds
issue warning
- Fixes#19528
## Solution
- Don't allow nightly lints
# Objective
- Currently there is predefinied list of supported DataTypes that can be
detected on Bevy JSON Schema generation and mapped as reflect_types
array elements.
- Make it possible to register custom `reflectTypes` mappings for Bevy
JSON Schema.
## Solution
- Create a `SchemaTypesMetadata` Resource that will hold mappings for
`TypeId` of `TypeData`. List is bigger from beggining and it is possible
to expand it without forking package.
## Testing
- I use it for quite a while in my game, I have a fork of bevy_remote
with more changes that later I want to merge to main as well.
---------
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
# Objective
- Alternative to #19721
- The old implementation had several issues:
- underexplained
- bit complicated in places
- did not follow the source as described
- camera moves away
- camera does not use noise
- camera nudges back after shake ends, which looks cinematic, but not
what you want in practice
All in all: the old implementation did not really show a typical
implementation IMO
## Solution
- Rewrite it :D
- I believe the current implementation is a robust thing you can learn
from or just copy-paste into your project
## Testing
https://github.com/user-attachments/assets/bfe74fb6-c428-4d5a-9c9c-cd4a034ba176
---------
Co-authored-by: Rob Parrett <robparrett@gmail.com>
# Objective
This is part of the "core widgets" effort:
https://github.com/bevyengine/bevy/issues/19236.
## Solution
This adds the "core checkbox" widget type.
## Testing
Tested using examples core_widgets and core_widgets_observers.
Note to reviewers: I reorganized the code in the examples, so the diffs
are large because of code moves.
# Objective
- Splitted off from #19491
- Make adding generated code to the `Bundle` derive macro easier
- Fix a bug when multiple fields are `#[bundle(ignore)]`
## Solution
- Instead of accumulating the code for each method in a different `Vec`,
accumulate only the names of non-ignored fields and their types, then
use `quote` to generate the code for each of them in the method body.
- To fix the bug, change the code populating the `BundleFieldKind` to
push only one of them per-field (previously each `#[bundle(ignore)]`
resulted in pushing twice, once for the correct
`BundleFieldKind::Ignore` and then again unconditionally for
`BundleFieldKind::Component`)
## Testing
- Added a regression test for the bug that was fixed
Custom derived `QueryData` impls currently generate `Item` structs with
the lifetimes swapped, which blows up the borrow checker sometimes.
See:
https://discord.com/channels/691052431525675048/749335865876021248/1385509416086011914
could add a regression test, TBH I don't know the error well enough to
do that minimally. Seems like it's that both lifetimes on
`QueryData::Item` need to be covariant, but I'm not sure.
# Objective
Unblock #18162.
#15396 added the `'s` lifetime to `QueryData::Item` to make it possible
for query items to borrow from the state. The state isn't passed
directly to `QueryData::fetch()`, so it also added the `'s` lifetime to
`WorldQuery::Fetch` so that we can pass the borrows through there.
Unfortunately, having `WorldQuery::Fetch` borrow from the state makes it
impossible to have owned state, because we store the state and the
`Fetch` in the same `struct` during iteration.
## Solution
Undo the change to add the `'s` lifetime to `WorldQuery::Fetch`.
Instead, add a `&'s Self::State` parameter to `QueryData::fetch()` and
`QueryFilter::filter_fetch()` so that borrows from the state can be
passed directly to query items.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Emerson Coskey <emerson@coskey.dev>
# Objective
- Many strings in bevy_ecs are created but only used for debug: system
name, component name, ...
- Those strings make a significant part of the final binary and are no
use in a released game
## Solution
- Use [`strings`](https://linux.die.net/man/1/strings) to find ...
strings in a binary
- Try to find where they come from
- Many are made from `type_name::<T>()` and only used in error / debug
messages
- Add a new structure `DebugName` that holds no value if `debug` feature
is disabled
- Replace `core::any::type_name::<T>()` by `DebugName::type_name::<T>()`
## Testing
Measurements were taken without the new feature being enabled by
default, to help with commands
### File Size
I tried building the `breakout` example with `cargo run --release
--example breakout`
|`debug` enabled|`debug` disabled|
|-|-|
|81621776 B|77735728B|
|77.84MB|74.13MB|
### Compilation time
`hyperfine --min-runs 15 --prepare "cargo clean && sleep 5"
'RUSTC_WRAPPER="" cargo build --release --example breakout'
'RUSTC_WRAPPER="" cargo build --release --example breakout --features
debug'`
```
breakout' 'RUSTC_WRAPPER="" cargo build --release --example breakout --features debug'
Benchmark 1: RUSTC_WRAPPER="" cargo build --release --example breakout
Time (mean ± σ): 84.856 s ± 3.565 s [User: 1093.817 s, System: 32.547 s]
Range (min … max): 78.038 s … 89.214 s 15 runs
Benchmark 2: RUSTC_WRAPPER="" cargo build --release --example breakout --features debug
Time (mean ± σ): 92.303 s ± 2.466 s [User: 1193.443 s, System: 33.803 s]
Range (min … max): 90.619 s … 99.684 s 15 runs
Summary
RUSTC_WRAPPER="" cargo build --release --example breakout ran
1.09 ± 0.05 times faster than RUSTC_WRAPPER="" cargo build --release --example breakout --features debug
```
# Objective
While `KeyCode` is very often the correct way to interact with keyboard
input there are a bunch of cases where it isn't, notably most of the
symbols (e.g. plus, minus, different parentheses). Currently the only
way to get these is to read from `EventReader<KeyboardInput>`, but then
you'd have to redo the `ButtonInput` logic for pressed/released to e.g.
make zoom functionality that depends on plus/minus keys.
This has led to confusion previously, like
https://github.com/bevyengine/bevy/issues/3278
## Solution
Add a `ButtonInput<Key>` resource.
## Testing
Modified the `keyboard_input` example to test it.
## Open questions
I'm not 100% sure this is the right way forward, since it duplicates the
key processing logic and might make people use the shorter
`ButtonInput<Key>` even when it's not appropriate.
Another option is to add a new struct with both `Key` and `KeyCode`, and
use `ButtonInput` with that instead. That would make it more
explanatory, but that is a lot of churn.
The third alternative is to not do this because it's too niche.
I'll add more documentation and take it out of draft if we want to move
forward with it.
# Objective
Fixes#18726
Alternative to and closes#18797
## Solution
Create a method `Observer::system_name` to expose the name of the
`Observer`'s system
## Showcase
```rust
// Returns `my_crate::my_observer`
let observer = Observer::new(my_observer);
println!(observer.system_name());
// Returns `my_crate::method::{{closure}}`
let observer = Observer::new(|_trigger: Trigger<...>|);
println!(observer.system_name());
// Returns `custom_name`
let observer = Observer::new(IntoSystem::into_system(my_observer).with_name("custom_name"));
println!(observer.system_name());
```
## TODO
- [ ] Achieve cart's approval
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
Fix https://github.com/bevyengine/bevy/issues/19642 by enabling e.g.
```
map.get_type::<MyType>();
```
in place of
```
map.get(&TypeId::of::<MyType>());
```
## Solution
Add an extension trait `TypeIdMapExt` with `insert_type`, `get_type`,
`get_type_mut` and `remove_type` counterparts for `insert`, `get`,
`get_mut` and `remove`.
## Testing
Doc test.
# Objective
- Fixes#19627
- Tackles part of #19644
- Supersedes #19629
- `Window` has become a very very very big component
- As such, our change detection does not *really* work on it, as e.g.
moving the mouse will cause a change for the entire window
- We circumvented this with a cache
- But, some things *shouldn't* be cached as they can be changed from
outside the user's control, notably the cursor grab mode on web
- So, we need to disable the cache for that
- But because change detection is broken, that would result in the
cursor grab mode being set every frame the mouse is moved
- That is usually *not* what a dev wants, as it forces the cursor to be
locked even when the end-user is trying to free the cursor on the
browser
- the cache in this situation is invalid due to #8949
## Solution
- Split `Window` into multiple components, each with working change
detection
- Disable caching of the cursor grab mode
- This will only attempt to force the grab mode when the `CursorOptions`
were touched by the user, which is *much* rarer than simply moving the
mouse.
- If this PR is merged, I'll do the exact same for the other
constituents of `Window` as a follow-up
## Testing
- Ran all the changed examples
Closes#19677.
I don't think that the output type needs to be `Send`. I've done some
test at it seems to work fine without it, which in IMO makes sense, but
please correct me if that is not the case.
# Objective
- compute_matrix doesn't compute anything, it just puts an Affine3A into
a Mat4. the name is inaccurate
## Solution
- rename it to conform with to_isometry (which, ironically, does compute
a decomposition which is rather expensive)
## Testing
- Its a rename. If it compiles, its good to go
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
- When trying to serialize an structure that contains `&'static str`
using only Reflection, I get the following error:
```
"type `&str` did not register the `ReflectSerialize` or `ReflectSerializeWithRegistry` type data.
For certain types, this may need to be registered manually using `register_type_data` (stack: ... -> `core::option::Option<&str>` -> `&str`)")
```
## Solution
- Register `ReflectSerialize` for `&str`
## Testing
- `cargo run -p ci`: OK
# Objective
The position for track clicks in `core_slider` is calculated incorrectly
when using `UiScale`.
## Solution
`trigger.event().pointer_location.position` uses logical window
coordinates, that is:
`position = physical_position / window_scale_factor`
while `ComputedNodeTarget::scale_factor` returns the window scale factor
multiplied by Ui Scale:
`target_scale_factor = window_scale_factor * ui_scale`
So to get the physical position need to divide by the `UiScale`:
```
position * target_scale_factor / ui_scale
= (physical_postion / window_scale_factor) * (window_scale_factor * ui_scale) / ui_scale
= physical_position
```
I thought this was fixed during the slider PR review, but must have got
missed somewhere or lost in a merge.
## Testing
Can test using the `core_widgets` example` with
`.insert_resource(UiScale(2.))` added to the bevy app.
# Objective
When the `CoreSlider`s `on_change` is set to None, Keyboard input, like
ArrowKeys, does not update the `SliderValue`.
## Solution
Handle the missing case, like it is done for Pointer.
## Testing
- Did you test these changes?
Yes: core_widgets & core_widgets_observers
in both examples one has to remove / comment out the setting of
`CoreSlider::on_change` to test the case of `on_change` being none.
- Are there any parts that need more testing?
No not that I am aware of.
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
Yes: core_widgets & core_widgets_observers
in both examples one has to remove / comment out the setting of
`CoreSlider::on_change` to test the case of `on_change` being none.
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
I tested on linux + wayland. But it is unlikely that it would effect
outcomes.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
- to_isometry is not a direct conversion, it involves computation. the
docs could be clearer
## Solution
- Improve docs
## Testing
- its docs
# Objective
Our strategy for storing observers is made up of several moving parts,
which are ultimately fairly simple nested HashMaps.
These types are currently `pub`, but lack any meaningful way to access
this data.
We have three options here:
1. Make these internals not `pub` at all.
2. Make the data read-only accessible.
3. Make the data mutably accessible.
## Solution
I've opted for option 2, exposing read-only values. This is consistent
with our existing approach to the ECS internals, allowing for easier
debugging without risking wanton data corruption. If one day you would
like to mutably access this data, please open an issue clearly
explaining what you're trying to do.
This was a pretty mechanical change, exposing fields via getters. I've
also opted to do my best to clarify some field names and documentation:
please double-check those for correctness. It was hard to be fully
confident, as the field names and documentation was not very clear ;)
## Testing
I spent some time going through the code paths, making sure that users
can trace all the way from `World` to the leaf nodes. Reviewers, please
ensure the same!
## Notes for reviewers
This is part of a broader observer overhaul: I fully expect us to change
up these internals and break these shiny new APIs. Probably even within
the same cycle!
But clean up your work area first: this sort of read-only getter and
improved docs will be important to replace as we work.
# Objective
*Fixes #5670 as an opt-in for now*
glTF uses the following coordinate system:
- forward: Z
- up: Y
- right: -X
and Bevy uses:
- forward: -Z
- up: Y
- right: X
For the longest time, Bevy has simply ignored this distinction. That
caused issues when working across programs, as most software respects
the
glTF coordinate system when importing and exporting glTFs. Your scene
might have looked correct in Blender, Maya, TrenchBroom, etc. but
everything would be flipped when importing it into Bevy!
## Solution
Add an option to the glTF loader to perform coordinate conversion. Note
that this makes a distinction in the camera nodes, as glTF uses a
different coordinate system for them.
## Follow Ups
- Add global glTF loader settings, similar to the image loader, so that
users can make third-party crates also load their glTFs with corrected
coordinates
- Decide on a migration strategy to make this the future default
- Create an issue
- Get feedback from Patrick Walton and Cart (not pinging them here to
not spam them)
- Include this pic for reference of how Blender assumes -Y as forward:

## Testing
I ran all glTF animation examples with the new setting enabled to
validate that they look the same, just flipped.
Also got a nice test scene from Chris that includes a camera inside the
glTF. Thanks @ChristopherBiscardi!
Blender (-Y forward):

Bevy (-Z forward, but the model looks the wrong way):

Bevy with `convert_coordinates` enabled (-Z forward):

Validation that the axes are correct with F3D's glTF viewer (+Z
forward):

# Objective
- Fix issue where `SubStates` depending on multiple source states would
only react when _all_ source states changed simultaneously.
- SubStates should be created/destroyed whenever _any_ of their source
states transitions, not only when all change together.
# Solution
- Changed the "did parent change" detection logic from AND to OR. We
need to check if _any_ of the event readers changed, not if _all_ of
them changed.
- See
https://github.com/bevyengine/bevy/actions/runs/15610159742/job/43968937544?pr=19595
for failing test proof before I pushed the fix.
- The generated code we want needs `||`s not `&&`s like this:
```rust
fn register_sub_state_systems_in_schedule<T: SubStates<SourceStates = Self>>(schedule: &mut Schedule) {
let apply_state_transition = |(mut ereader0, mut ereader1, mut ereader2): (
EventReader<StateTransitionEvent<S0::RawState>>,
EventReader<StateTransitionEvent<S1::RawState>>,
EventReader<StateTransitionEvent<S2::RawState>>,
),
event: EventWriter<StateTransitionEvent<T>>,
commands: Commands,
current_state_res: Option<ResMut<State<T>>>,
next_state_res: Option<ResMut<NextState<T>>>,
(s0, s1, s2): (
Option<Res<State<S0::RawState>>>,
Option<Res<State<S1::RawState>>>,
Option<Res<State<S2::RawState>>>,
)| {
// With `||` we can correctly count parent changed if any of the sources changed.
let parent_changed = (ereader0.read().last().is_some()
|| ereader1.read().last().is_some()
|| ereader2.read().last().is_some());
let next_state = take_next_state(next_state_res);
if !parent_changed && next_state.is_none() {
return;
}
// ...
}
}
```
# Testing
- Add new test.
- Check the fix worked in my game.
# Objective
Current way to wire `Layer`s together using `layer.with(new_layer)` in
the `bevy_log` plugin is brittle and not flexible. As #17722
demonstrated, the current solution makes it very hard to do any kind of
advanced wiring, as the type system of `tracing::Subscriber` gets in the
way very quickly (the type of each new layer depends on the type of the
previous ones). We want to make it easier to have more complex wiring of
`Layers`. It would be hard to solve #19085 without it
## Solution
It aims to be functionally equivalent.
- Replace of using `layer.with(new_layer)` . We now add `layer.boxed()`
to a `Vec<BoxedLayer>`. It is a solution recommended by
`tracing_subscriber::Layer` for complex wiring cases (See
https://docs.rs/tracing-subscriber/latest/tracing_subscriber/layer/index.html#runtime-configuration-with-layers)
- Do some refactoring and clean up that is now enabled by the new
solution
## Testing
- Ran CI locally on Linux
- Ran the logs examples
- Need people familiar with the features `trace`, `tracing-chrome`,
`tracing-tracy` to check that it still works as expected
- Need people with access to `ios`, `android` and `wasm` to check it as
well.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Kristoffer Søholm <k.soeholm@gmail.com>
# Objective
I have a custom asset loader, and need access to the error it reports
when failing to load (e.g. through `AssetLoadFailedEvent { error:
AssetLoadError::AssetLoaderError(loader_error), .. }`). However
`AssetLoaderError` doesn't expose its `<core::error::Error>::source()`
(i.e. its `error` field. It only formats it when `Display`ed.
*I haven't searched for issues about it.*
## Solution
- Annotate `AssetLoaderError`'s `error` field with `#[source]`.
- Don't include the error when `AssetLoaderError` is `Display`ed (when
one prints an error's source stack like a backtrace, it would now be
dupplicated).
- (optional, included as a separated commit) Add a getter for the `&dyn
Error` stored in the `error` field (whithin an `Arc`). This is more
ergonomic than using `Error::source()` because it casts an `&Arc<dyn
Error>` into an `&dyn Error`, meaning one has to downcast it twice to
get the original error from the loader, including once where you have to
specify the correct type of the *private* `error` field. So downcasting
from `Error::source()` effectively rely on the internal implementation
of `AssetLoaderError`. The getter instead return the trait object
directly, which mean it will directly downcast to the expected loader
error type.
I didn't included a test that checks that double-downcasting
`<AssetLoaderError as Error>::source()` doesn't break user code that
would rely on the private field's type.
## Testing
- Downcasting the trait objects for both `source()` and the `error()`
getter work as described above.
- `cargo test -p bevy_asset --all-features` pass without errors.
---------
Co-authored-by: austreelis <git@swhaele.net>
# Objective
Fixes#16051Closes#16145
## Solution
Allow passing `--build-jobs` and `--test-threads` to `ci`
i.e.
```
cargo run -p ci -- --build-jobs 4 --test-threads 4
```
## Testing
running ci locally
---------
Co-authored-by: Benjamin Brienen <Benjamin.Brienen@outlook.com>
# Objective
Improve the performance of `FilteredEntity(Ref|Mut)` and
`Entity(Ref|Mut)Except`.
`FilteredEntityRef` needs an `Access<ComponentId>` to determine what
components it can access. There is one stored in the query state, but
query items cannot borrow from the state, so it has to `clone()` the
access for each row. Cloning the access involves memory allocations and
can be expensive.
## Solution
Let query items borrow from their query state.
Add an `'s` lifetime to `WorldQuery::Item` and `WorldQuery::Fetch`,
similar to the one in `SystemParam`, and provide `&'s Self::State` to
the fetch so that it can borrow from the state.
Unfortunately, there are a few cases where we currently return query
items from temporary query states: the sorted iteration methods create a
temporary state to query the sort keys, and the
`EntityRef::components<Q>()` methods create a temporary state for their
query.
To allow these to continue to work with most `QueryData`
implementations, introduce a new subtrait `ReleaseStateQueryData` that
converts a `QueryItem<'w, 's>` to `QueryItem<'w, 'static>`, and is
implemented for everything except `FilteredEntity(Ref|Mut)` and
`Entity(Ref|Mut)Except`.
`#[derive(QueryData)]` will generate `ReleaseStateQueryData`
implementations that apply when all of the subqueries implement
`ReleaseStateQueryData`.
This PR does not actually change the implementation of
`FilteredEntity(Ref|Mut)` or `Entity(Ref|Mut)Except`! That will be done
as a follow-up PR so that the changes are easier to review. I have
pushed the changes as chescock/bevy#5.
## Testing
I ran performance traces of many_foxes, both against main and against
chescock/bevy#5, both including #15282. These changes do appear to make
generalized animation a bit faster:
(Red is main, yellow is chescock/bevy#5)

## Migration Guide
The `WorldQuery::Item` and `WorldQuery::Fetch` associated types and the
`QueryItem` and `ROQueryItem` type aliases now have an additional
lifetime parameter corresponding to the `'s` lifetime in `Query`. Manual
implementations of `WorldQuery` will need to update the method
signatures to include the new lifetimes. Other uses of the types will
need to be updated to include a lifetime parameter, although it can
usually be passed as `'_`. In particular, `ROQueryItem` is used when
implementing `RenderCommand`.
Before:
```rust
fn render<'w>(
item: &P,
view: ROQueryItem<'w, Self::ViewQuery>,
entity: Option<ROQueryItem<'w, Self::ItemQuery>>,
param: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult;
```
After:
```rust
fn render<'w>(
item: &P,
view: ROQueryItem<'w, '_, Self::ViewQuery>,
entity: Option<ROQueryItem<'w, '_, Self::ItemQuery>>,
param: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult;
```
---
Methods on `QueryState` that take `&mut self` may now result in
conflicting borrows if the query items capture the lifetime of the
mutable reference. This affects `get()`, `iter()`, and others. To fix
the errors, first call `QueryState::update_archetypes()`, and then
replace a call `state.foo(world, param)` with
`state.query_manual(world).foo_inner(param)`. Alternately, you may be
able to restructure the code to call `state.query(world)` once and then
make multiple calls using the `Query`.
Before:
```rust
let mut state: QueryState<_, _> = ...;
let d1 = state.get(world, e1);
let d2 = state.get(world, e2); // Error: cannot borrow `state` as mutable more than once at a time
println!("{d1:?}");
println!("{d2:?}");
```
After:
```rust
let mut state: QueryState<_, _> = ...;
state.update_archetypes(world);
let d1 = state.get_manual(world, e1);
let d2 = state.get_manual(world, e2);
// OR
state.update_archetypes(world);
let d1 = state.query(world).get_inner(e1);
let d2 = state.query(world).get_inner(e2);
// OR
let query = state.query(world);
let d1 = query.get_inner(e1);
let d1 = query.get_inner(e2);
println!("{d1:?}");
println!("{d2:?}");
```
# Objective
Getting access to the original target of an entity-event is really
helpful when working with bubbled / propagated events.
`bevy_picking` special-cases this, but users have requested this for all
sorts of bubbled events.
The existing naming convention was also very confusing. Fixes
https://github.com/bevyengine/bevy/issues/17112, but also see #18982.
## Solution
1. Rename `ObserverTrigger::target` -> `current_target`.
1. Store `original_target: Option<Entity>` in `ObserverTrigger`.
1. Wire it up so this field gets set correctly.
1. Remove the `target` field on the `Pointer` events from
`bevy_picking`.
Closes https://github.com/bevyengine/bevy/pull/18710, which attempted
the same thing. Thanks @emfax!
## Testing
I've modified an existing test to check that the entities returned
during event bubbling / propagation are correct.
## Notes to reviewers
It's a little weird / sad that you can no longer access this infromation
via the buffered events for `Pointer`. That said, you already couldn't
access any bubbled target. We should probably remove the `BufferedEvent`
form of `Pointer` to reduce confusion and overhead, but I didn't want to
do so here.
Observer events can be trivially converted into buffered events (write
an observer with an EventWriter), and I suspect that that is the better
migration if you want the controllable timing or performance
characteristics of buffered events for your specific use case.
## Future work
It would be nice to not store this data at all (and not expose any
methods) if propagation was disabled. That involves more trait
shuffling, and I don't think we should do it here for reviewability.
---------
Co-authored-by: Joona Aalto <jondolf.dev@gmail.com>
# Objective
- Try to make more of `bevy_color` const now that we have
const_float_arithmetic.
## Solution
Fail abjectly, because of our heavy use of traits.
I did find these functions though, so you can have a PR 🙃
# Objective
- `remove_child` was mentioned missing in #19556 and I realized that
`insert_child` was also missing.
- Removes the need to wrap a single entity with `&[]` with
`remove_children` and `insert_children`
- Would have also added `despawn_children` but #19283 does so.
## Solution
- Simple wrapper around `remove_related`
## Testing
- Added `insert_child` and `remove_child` tests analgous to
`insert_children` and `remove_children` and then ran `cargo run -p ci --
test`
# Objective
As someone who is currently learning Bevy, I found the implementation of
the ambient light in the 3d/lighting.rs example unsatisfactory.
## Solution
- I adjusted the brightness of the ambient light in the scene to 200
(where the default is 80). It was previously 0.02, a value so low it has
no noticeable effect.
- I added a keybind (space bar) to toggle the ambient light, allowing
users to see the difference it makes. I also added text showing the
state of the ambient light (on, off) and text showing the keybind.
I'm very new to Bevy and Rust, so apologies if any of this code is not
up to scratch.
## Testing
I checked all the text still updates correctly and all keybinds still
work. In my testing, it looks to work okay.
I'd appreciate others testing too, just to make sure.
---
## Showcase
<details>
<summary>Click to view showcase</summary>
<img width="960" alt="Screenshot (11)"
src="https://github.com/user-attachments/assets/916e569e-cd49-43fd-b81d-aae600890cd3"
/>
<img width="959" alt="Screenshot (12)"
src="https://github.com/user-attachments/assets/0e16bb3a-c38a-4a8d-8248-edf3b820d238"
/>
</details>
# Objective
Closes#19564.
The current `Event` trait looks like this:
```rust
pub trait Event: Send + Sync + 'static {
type Traversal: Traversal<Self>;
const AUTO_PROPAGATE: bool = false;
fn register_component_id(world: &mut World) -> ComponentId { ... }
fn component_id(world: &World) -> Option<ComponentId> { ... }
}
```
The `Event` trait is used by both buffered events
(`EventReader`/`EventWriter`) and observer events. If they are observer
events, they can optionally be targeted at specific `Entity`s or
`ComponentId`s, and can even be propagated to other entities.
However, there has long been a desire to split the trait semantically
for a variety of reasons, see #14843, #14272, and #16031 for discussion.
Some reasons include:
- It's very uncommon to use a single event type as both a buffered event
and targeted observer event. They are used differently and tend to have
distinct semantics.
- A common footgun is using buffered events with observers or event
readers with observer events, as there is no type-level error that
prevents this kind of misuse.
- #19440 made `Trigger::target` return an `Option<Entity>`. This
*seriously* hurts ergonomics for the general case of entity observers,
as you need to `.unwrap()` each time. If we could statically determine
whether the event is expected to have an entity target, this would be
unnecessary.
There's really two main ways that we can categorize events: push vs.
pull (i.e. "observer event" vs. "buffered event") and global vs.
targeted:
| | Push | Pull |
| ------------ | --------------- | --------------------------- |
| **Global** | Global observer | `EventReader`/`EventWriter` |
| **Targeted** | Entity observer | - |
There are many ways to approach this, each with their tradeoffs.
Ultimately, we kind of want to split events both ways:
- A type-level distinction between observer events and buffered events,
to prevent people from using the wrong kind of event in APIs
- A statically designated entity target for observer events to avoid
accidentally using untargeted events for targeted APIs
This PR achieves these goals by splitting event traits into `Event`,
`EntityEvent`, and `BufferedEvent`, with `Event` being the shared trait
implemented by all events.
## `Event`, `EntityEvent`, and `BufferedEvent`
`Event` is now a very simple trait shared by all events.
```rust
pub trait Event: Send + Sync + 'static {
// Required for observer APIs
fn register_component_id(world: &mut World) -> ComponentId { ... }
fn component_id(world: &World) -> Option<ComponentId> { ... }
}
```
You can call `trigger` for *any* event, and use a global observer for
listening to the event.
```rust
#[derive(Event)]
struct Speak {
message: String,
}
// ...
app.add_observer(|trigger: On<Speak>| {
println!("{}", trigger.message);
});
// ...
commands.trigger(Speak {
message: "Y'all like these reworked events?".to_string(),
});
```
To allow an event to be targeted at entities and even propagated
further, you can additionally implement the `EntityEvent` trait:
```rust
pub trait EntityEvent: Event {
type Traversal: Traversal<Self>;
const AUTO_PROPAGATE: bool = false;
}
```
This lets you call `trigger_targets`, and to use targeted observer APIs
like `EntityCommands::observe`:
```rust
#[derive(Event, EntityEvent)]
#[entity_event(traversal = &'static ChildOf, auto_propagate)]
struct Damage {
amount: f32,
}
// ...
let enemy = commands.spawn((Enemy, Health(100.0))).id();
// Spawn some armor as a child of the enemy entity.
// When the armor takes damage, it will bubble the event up to the enemy.
let armor_piece = commands
.spawn((ArmorPiece, Health(25.0), ChildOf(enemy)))
.observe(|trigger: On<Damage>, mut query: Query<&mut Health>| {
// Note: `On::target` only exists because this is an `EntityEvent`.
let mut health = query.get(trigger.target()).unwrap();
health.0 -= trigger.amount();
});
commands.trigger_targets(Damage { amount: 10.0 }, armor_piece);
```
> [!NOTE]
> You *can* still also trigger an `EntityEvent` without targets using
`trigger`. We probably *could* make this an either-or thing, but I'm not
sure that's actually desirable.
To allow an event to be used with the buffered API, you can implement
`BufferedEvent`:
```rust
pub trait BufferedEvent: Event {}
```
The event can then be used with `EventReader`/`EventWriter`:
```rust
#[derive(Event, BufferedEvent)]
struct Message(String);
fn write_hello(mut writer: EventWriter<Message>) {
writer.write(Message("I hope these examples are alright".to_string()));
}
fn read_messages(mut reader: EventReader<Message>) {
// Process all buffered events of type `Message`.
for Message(message) in reader.read() {
println!("{message}");
}
}
```
In summary:
- Need a basic event you can trigger and observe? Derive `Event`!
- Need the event to be targeted at an entity? Derive `EntityEvent`!
- Need the event to be buffered and support the
`EventReader`/`EventWriter` API? Derive `BufferedEvent`!
## Alternatives
I'll now cover some of the alternative approaches I have considered and
briefly explored. I made this section collapsible since it ended up
being quite long :P
<details>
<summary>Expand this to see alternatives</summary>
### 1. Unified `Event` Trait
One option is not to have *three* separate traits (`Event`,
`EntityEvent`, `BufferedEvent`), and to instead just use associated
constants on `Event` to determine whether an event supports targeting
and buffering or not:
```rust
pub trait Event: Send + Sync + 'static {
type Traversal: Traversal<Self>;
const AUTO_PROPAGATE: bool = false;
const TARGETED: bool = false;
const BUFFERED: bool = false;
fn register_component_id(world: &mut World) -> ComponentId { ... }
fn component_id(world: &World) -> Option<ComponentId> { ... }
}
```
Methods can then use bounds like `where E: Event<TARGETED = true>` or
`where E: Event<BUFFERED = true>` to limit APIs to specific kinds of
events.
This would keep everything under one `Event` trait, but I don't think
it's necessarily a good idea. It makes APIs harder to read, and docs
can't easily refer to specific types of events. You can also create
weird invariants: what if you specify `TARGETED = false`, but have
`Traversal` and/or `AUTO_PROPAGATE` enabled?
### 2. `Event` and `Trigger`
Another option is to only split the traits between buffered events and
observer events, since that is the main thing people have been asking
for, and they have the largest API difference.
If we did this, I think we would need to make the terms *clearly*
separate. We can't really use `Event` and `BufferedEvent` as the names,
since it would be strange that `BufferedEvent` doesn't implement
`Event`. Something like `ObserverEvent` and `BufferedEvent` could work,
but it'd be more verbose.
For this approach, I would instead keep `Event` for the current
`EventReader`/`EventWriter` API, and call the observer event a
`Trigger`, since the "trigger" terminology is already used in the
observer context within Bevy (both as a noun and a verb). This is also
what a long [bikeshed on
Discord](https://discord.com/channels/691052431525675048/749335865876021248/1298057661878898791)
seemed to land on at the end of last year.
```rust
// For `EventReader`/`EventWriter`
pub trait Event: Send + Sync + 'static {}
// For observers
pub trait Trigger: Send + Sync + 'static {
type Traversal: Traversal<Self>;
const AUTO_PROPAGATE: bool = false;
const TARGETED: bool = false;
fn register_component_id(world: &mut World) -> ComponentId { ... }
fn component_id(world: &World) -> Option<ComponentId> { ... }
}
```
The problem is that "event" is just a really good term for something
that "happens". Observers are rapidly becoming the more prominent API,
so it'd be weird to give them the `Trigger` name and leave the good
`Event` name for the less common API.
So, even though a split like this seems neat on the surface, I think it
ultimately wouldn't really work. We want to keep the `Event` name for
observer events, and there is no good alternative for the buffered
variant. (`Message` was suggested, but saying stuff like "sends a
collision message" is weird.)
### 3. `GlobalEvent` + `TargetedEvent`
What if instead of focusing on the buffered vs. observed split, we
*only* make a distinction between global and targeted events?
```rust
// A shared event trait to allow global observers to work
pub trait Event: Send + Sync + 'static {
fn register_component_id(world: &mut World) -> ComponentId { ... }
fn component_id(world: &World) -> Option<ComponentId> { ... }
}
// For buffered events and non-targeted observer events
pub trait GlobalEvent: Event {}
// For targeted observer events
pub trait TargetedEvent: Event {
type Traversal: Traversal<Self>;
const AUTO_PROPAGATE: bool = false;
}
```
This is actually the first approach I implemented, and it has the neat
characteristic that you can only use non-targeted APIs like `trigger`
with a `GlobalEvent` and targeted APIs like `trigger_targets` with a
`TargetedEvent`. You have full control over whether the entity should or
should not have a target, as they are fully distinct at the type-level.
However, there's a few problems:
- There is no type-level indication of whether a `GlobalEvent` supports
buffered events or just non-targeted observer events
- An `Event` on its own does literally nothing, it's just a shared trait
required to make global observers accept both non-targeted and targeted
events
- If an event is both a `GlobalEvent` and `TargetedEvent`, global
observers again have ambiguity on whether an event has a target or not,
undermining some of the benefits
- The names are not ideal
### 4. `Event` and `EntityEvent`
We can fix some of the problems of Alternative 3 by accepting that
targeted events can also be used in non-targeted contexts, and simply
having the `Event` and `EntityEvent` traits:
```rust
// For buffered events and non-targeted observer events
pub trait Event: Send + Sync + 'static {
fn register_component_id(world: &mut World) -> ComponentId { ... }
fn component_id(world: &World) -> Option<ComponentId> { ... }
}
// For targeted observer events
pub trait EntityEvent: Event {
type Traversal: Traversal<Self>;
const AUTO_PROPAGATE: bool = false;
}
```
This is essentially identical to this PR, just without a dedicated
`BufferedEvent`. The remaining major "problem" is that there is still
zero type-level indication of whether an `Event` event *actually*
supports the buffered API. This leads us to the solution proposed in
this PR, using `Event`, `EntityEvent`, and `BufferedEvent`.
</details>
## Conclusion
The `Event` + `EntityEvent` + `BufferedEvent` split proposed in this PR
aims to solve all the common problems with Bevy's current event model
while keeping the "weirdness" factor minimal. It splits in terms of both
the push vs. pull *and* global vs. targeted aspects, while maintaining a
shared concept for an "event".
### Why I Like This
- The term "event" remains as a single concept for all the different
kinds of events in Bevy.
- Despite all event types being "events", they use fundamentally
different APIs. Instead of assuming that you can use an event type with
any pattern (when only one is typically supported), you explicitly opt
in to each one with dedicated traits.
- Using separate traits for each type of event helps with documentation
and clearer function signatures.
- I can safely make assumptions on expected usage.
- If I see that an event is an `EntityEvent`, I can assume that I can
use `observe` on it and get targeted events.
- If I see that an event is a `BufferedEvent`, I can assume that I can
use `EventReader` to read events.
- If I see both `EntityEvent` and `BufferedEvent`, I can assume that
both APIs are supported.
In summary: This allows for a unified concept for events, while limiting
the different ways to use them with opt-in traits. No more guess-work
involved when using APIs.
### Problems?
- Because `BufferedEvent` implements `Event` (for more consistent
semantics etc.), you can still use all buffered events for non-targeted
observers. I think this is fine/good. The important part is that if you
see that an event implements `BufferedEvent`, you know that the
`EventReader`/`EventWriter` API should be supported. Whether it *also*
supports other APIs is secondary.
- I currently only support `trigger_targets` for an `EntityEvent`.
However, you can technically target components too, without targeting
any entities. I consider that such a niche and advanced use case that
it's not a huge problem to only support it for `EntityEvent`s, but we
could also split `trigger_targets` into `trigger_entities` and
`trigger_components` if we wanted to (or implement components as
entities :P).
- You can still trigger an `EntityEvent` *without* targets. I consider
this correct, since `Event` implements the non-targeted behavior, and
it'd be weird if implementing another trait *removed* behavior. However,
it does mean that global observers for entity events can technically
return `Entity::PLACEHOLDER` again (since I got rid of the
`Option<Entity>` added in #19440 for ergonomics). I think that's enough
of an edge case that it's not a huge problem, but it is worth keeping in
mind.
- ~~Deriving both `EntityEvent` and `BufferedEvent` for the same type
currently duplicates the `Event` implementation, so you instead need to
manually implement one of them.~~ Changed to always requiring `Event` to
be derived.
## Related Work
There are plans to implement multi-event support for observers,
especially for UI contexts. [Cart's
example](https://github.com/bevyengine/bevy/issues/14649#issuecomment-2960402508)
API looked like this:
```rust
// Truncated for brevity
trigger: Trigger<(
OnAdd<Pressed>,
OnRemove<Pressed>,
OnAdd<InteractionDisabled>,
OnRemove<InteractionDisabled>,
OnInsert<Hovered>,
)>,
```
I believe this shouldn't be in conflict with this PR. If anything, this
PR might *help* achieve the multi-event pattern for entity observers
with fewer footguns: by statically enforcing that all of these events
are `EntityEvent`s in the context of `EntityCommands::observe`, we can
avoid misuse or weird cases where *some* events inside the trigger are
targeted while others are not.
# Objective
- A step towards #19024.
- Allow `ReflectAsset` to work with any `AssetId` not just `Handle`.
- `ReflectAsset::ids()` returns an iterator of `AssetId`s, but then
there's no way to use these ids, since all the other APIs in
`ReflectAsset` require a handle (and we don't have a reflect way to get
the handle).
## Solution
- Replace the `UntypedHandle` argument in `ReflectAsset` methods with
`impl Into<UntypedAssetId>`.
- This matches the regular asset API.
- This allows `ReflectAsset::ids()` to be more useful.
## Testing
- None.
# Objective
The methods and commands `replace_related` and
`replace_related_with_difference` may cause data stored at the
`RelationshipTarget` be lost when all original children are removed
before new children are added.
Part of https://github.com/bevyengine/bevy/issues/19589
## Solution
Fix the issue, either by removing the old children _after_ adding the
new ones and not _before_ (`replace_related_with_difference`) or by
taking the whole `RelationshipTarget` to modify it, not only the inner
collection (`replace_related`).
## Testing
I added a new test asserting the data is kept. I also added a general
test of these methods as they had none previously.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>