create-pull-request/patch
141 Commits
Author | SHA1 | Message | Date | |
---|---|---|---|---|
![]() |
38c3423693
|
Event Split: Event , EntityEvent , and BufferedEvent (#19647)
# 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. |
||
![]() |
f47b1c00ee
|
Bump ron version to 0.10. (#19631)
# Objective - Update ron to the latest version. - This is blocking changes to AnimationGraph (as some valid structs are not capable of being deserialized). ## Solution - Bump ron! ## Testing - The particular issue I was blocked by seems to be resolved! |
||
![]() |
e5dc177b4b
|
Rename Trigger to On (#19596)
# Objective Currently, the observer API looks like this: ```rust app.add_observer(|trigger: Trigger<Explode>| { info!("Entity {} exploded!", trigger.target()); }); ``` Future plans for observers also include "multi-event observers" with a trigger that looks like this (see [Cart's example](https://github.com/bevyengine/bevy/issues/14649#issuecomment-2960402508)): ```rust trigger: Trigger<( OnAdd<Pressed>, OnRemove<Pressed>, OnAdd<InteractionDisabled>, OnRemove<InteractionDisabled>, OnInsert<Hovered>, )>, ``` In scenarios like this, there is a lot of repetition of `On`. These are expected to be very high-traffic APIs especially in UI contexts, so ergonomics and readability are critical. By renaming `Trigger` to `On`, we can make these APIs read more cleanly and get rid of the repetition: ```rust app.add_observer(|trigger: On<Explode>| { info!("Entity {} exploded!", trigger.target()); }); ``` ```rust trigger: On<( Add<Pressed>, Remove<Pressed>, Add<InteractionDisabled>, Remove<InteractionDisabled>, Insert<Hovered>, )>, ``` Names like `On<Add<Pressed>>` emphasize the actual event listener nature more than `Trigger<OnAdd<Pressed>>`, and look cleaner. This *also* frees up the `Trigger` name if we want to use it for the observer event type, splitting them out from buffered events (bikeshedding this is out of scope for this PR though). For prior art: [`bevy_eventlistener`](https://github.com/aevyrie/bevy_eventlistener) used [`On`](https://docs.rs/bevy_eventlistener/latest/bevy_eventlistener/event_listener/struct.On.html) for its event listener type. Though in our case, the observer is the event listener, and `On` is just a type containing information about the triggered event. ## Solution Steal from `bevy_event_listener` by @aevyrie and use `On`. - Rename `Trigger` to `On` - Rename `OnAdd` to `Add` - Rename `OnInsert` to `Insert` - Rename `OnReplace` to `Replace` - Rename `OnRemove` to `Remove` - Rename `OnDespawn` to `Despawn` ## Discussion ### Naming Conflicts?? Using a name like `Add` might initially feel like a very bad idea, since it risks conflict with `core::ops::Add`. However, I don't expect this to be a big problem in practice. - You rarely need to actually implement the `Add` trait, especially in modules that would use the Bevy ECS. - In the rare cases where you *do* get a conflict, it is very easy to fix by just disambiguating, for example using `ops::Add`. - The `Add` event is a struct while the `Add` trait is a trait (duh), so the compiler error should be very obvious. For the record, renaming `OnAdd` to `Add`, I got exactly *zero* errors or conflicts within Bevy itself. But this is of course not entirely representative of actual projects *using* Bevy. You might then wonder, why not use `Added`? This would conflict with the `Added` query filter, so it wouldn't work. Additionally, the current naming convention for observer events does not use past tense. ### Documentation This does make documentation slightly more awkward when referring to `On` or its methods. Previous docs often referred to `Trigger::target` or "sends a `Trigger`" (which is... a bit strange anyway), which would now be `On::target` and "sends an observer `Event`". You can see the diff in this PR to see some of the effects. I think it should be fine though, we may just need to reword more documentation to read better. |
||
![]() |
02fa833be1
|
Rename JustifyText to Justify (#19522)
# Objective Rename `JustifyText`: * The name `JustifyText` is just ugly. * It's inconsistent since no other `bevy_text` types have a `Text-` suffix, only prefix. * It's inconsistent with the other text layout enum `Linebreak` which doesn't have a prefix or suffix. Fixes #19521. ## Solution Rename `JustifyText` to `Justify`. Without other context, it's natural to assume the name `Justify` refers to text justification. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
064e5e48b4
|
Remove entity placeholder from observers (#19440)
# Objective `Entity::PLACEHOLDER` acts as a magic number that will *probably* never really exist, but it certainly could. And, `Entity` has a niche, so the only reason to use `PLACEHOLDER` is as an alternative to `MaybeUninit` that trades safety risks for logic risks. As a result, bevy has generally advised against using `PLACEHOLDER`, but we still use if for a lot internally. This pr starts removing internal uses of it, starting from observers. ## Solution Change all trigger target related types from `Entity` to `Option<Entity>` Small migration guide to come. ## Testing CI ## Future Work This turned a lot of code from ```rust trigger.target() ``` to ```rust trigger.target().unwrap() ``` The extra panic is no worse than before; it's just earlier than panicking after passing the placeholder to something else. But this is kinda annoying. I would like to add a `TriggerMode` or something to `Event` that would restrict what kinds of targets can be used for that event. Many events like `Removed` etc, are always triggered with a target. We can make those have a way to assume Some, etc. But I wanted to save that for a future pr. |
||
![]() |
ddee5cca85
|
Improve Bevy's double-precision story for third-party crates (#19194)
# Objective Certain classes of games, usually those with enormous worlds, require some amount of support for double-precision. Libraries like `big_space` exist to allow for large worlds while integrating cleanly with Bevy's primarily single-precision ecosystem, but even then, games will often still work directly in double-precision throughout the part of the pipeline that feeds into the Bevy interface. Currently, working with double-precision types in Bevy is a pain. `glam` provides types like `DVec3`, but Bevy doesn't provide double-precision analogs for `glam` wrappers like `Dir3`. This is mostly because doing so involves one of: - code duplication - generics - templates (like `glam` uses) - macros Each of these has issues that are enough to be deal-breakers as far as maintainability, usability or readability. To work around this, I'm putting together `bevy_dmath`, a crate that duplicates `bevy_math` types and functionality to allow downstream users to enjoy the ergonomics and power of `bevy_math` in double-precision. For the most part, it's a smooth process, but in order to fully integrate, there are some necessary changes that can only be made in `bevy_math`. ## Solution This PR addresses the first and easiest issue with downstream double-precision math support: `VectorSpace` currently can only represent vector spaces over `f32`. This automatically closes the door to double-precision curves, among other things. This restriction can be easily lifted by allowing vector spaces to specify the underlying scalar field. This PR adds a new trait `ScalarField` that satisfies the properties of a scalar field (the ones that can be upheld statically) and adds a new associated type `type Scalar: ScalarField` to `VectorSpace`. It's mostly an unintrusive change. The biggest annoyances are: - it touches a lot of curve code - `bevy_math::ops` doesn't support `f64`, so there are some annoying workarounds As far as curves code, I wanted to make this change unintrusive and bite-sized, so I'm trying to touch as little code as possible. To prove to myself it can be done, I went ahead and (*not* in this PR) migrated most of the curves API to support different `ScalarField`s and it went really smoothly! The ugliest thing was adding `P::Scalar: From<usize>` in several places. There's an argument to be made here that we should be using `num-traits`, but that's not immediately relevant. The point is that for now, the smallest change I could make was to go into every curve impl and make them generic over `VectorSpace<Scalar = f32>`. Curves work exactly like before and don't change the user API at all. # Follow-up - **Extend `bevy_math::ops` to work with `f64`.** `bevy_math::ops` is used all over, and if curves are ever going to support different `ScalarField` types, we'll need to be able to use the correct `std` or `libm` ops for `f64` types as well. Adding an `ops64` mod turned out to be really ugly, but I'll point out the maintenance burden is low because we're not going to be adding new floating-point ops anytime soon. Another solution is to build a floating-point trait that calls the right op variant and impl it for `f32` and `f64`. This reduces maintenance burden because on the off chance we ever *do* want to go modify it, it's all tied together: you can't change the interface on one without changing the trait, which forces you to update the other. A third option is to use `num-traits`, which is basically option 2 but someone else did the work for us. They already support `no_std` using `libm`, so it would be more or less a drop-in replacement. They're missing a couple floating-point ops like `floor` and `ceil`, but we could make our own floating-point traits for those (there's even the potential for upstreaming them into `num-traits`). - **Tweak curves to accept vector spaces over any `ScalarField`.** Curves are ready to support custom scalar types as soon as the bullet above is addressed. I will admit that the code is not as fun to look at: `P::Scalar` instead of `f32` everywhere. We could consider an alternate design where we use `f32` even to interpolate something like a `DVec3`, but personally I think that's a worse solution than parameterizing curves over the vector space's scalar type. At the end of the day, it's not really bad to deal with in my opinion... `ScalarType` supports enough operations that working with them is almost like working with raw float types, and it unlocks a whole ecosystem for games that want to use double-precision. |
||
![]() |
7ab00ca185
|
Split Camera.hdr out into a new component (#18873)
# Objective - Simplify `Camera` initialization - allow effects to require HDR ## Solution - Split out `Camera.hdr` into a marker `Hdr` component ## Testing - ran `bloom_3d` example --- ## Showcase ```rs // before commands.spawn(( Camera3d Camera { hdr: true ..Default::default() } )) // after commands.spawn((Camera3d, Hdr)); // other rendering components can require that the camera enables hdr! // currently implemented for Bloom, AutoExposure, and Atmosphere. #[require(Hdr)] pub struct Bloom; ``` |
||
![]() |
b641aa0ecf
|
separate border colors (#18682)
# Objective allow specifying the left/top/right/bottom border colors separately for ui elements fixes #14773 ## Solution - change `BorderColor` to ```rs pub struct BorderColor { pub left: Color, pub top: Color, pub right: Color, pub bottom: Color, } ``` - generate one ui node per distinct border color, set flags for the active borders - render only the active borders i chose to do this rather than adding multiple colors to the ExtractedUiNode in order to minimize the impact for the common case where all border colors are the same. ## Testing modified the `borders` example to use separate colors:  the behaviour is a bit weird but it mirrors html/css border behaviour. --- ## Migration: To keep the existing behaviour, just change `BorderColor(color)` into `BorderColor::all(color)`. --------- Co-authored-by: ickshonpe <david.curthoys@googlemail.com> |
||
![]() |
d8fa57bd7b
|
Switch ChildOf back to tuple struct (#18672)
# Objective In #17905 we swapped to a named field on `ChildOf` to help resolve variable naming ambiguity of child vs parent (ex: `child_of.parent` clearly reads as "I am accessing the parent of the child_of relationship", whereas `child_of.0` is less clear). Unfortunately this has the side effect of making initialization less ideal. `ChildOf { parent }` reads just as well as `ChildOf(parent)`, but `ChildOf { parent: root }` doesn't read nearly as well as `ChildOf(root)`. ## Solution Move back to `ChildOf(pub Entity)` but add a `child_of.parent()` function and use it for all accesses. The downside here is that users are no longer "forced" to access the parent field with `parent` nomenclature, but I think this strikes the right balance. Take a look at the diff. I think the results provide strong evidence for this change. Initialization has the benefit of reading much better _and_ of taking up significantly less space, as many lines go from 3 to 1, and we're cutting out a bunch of syntax in some cases. Sadly I do think this should land in 0.16 as the cost of doing this _after_ the relationships migration is high. |
||
![]() |
54701a844e
|
Revert "Replace Ambient Lights with Environment Map Lights (#17482)" (#18167)
This reverts commit
|
||
![]() |
0b5302d96a
|
Replace Ambient Lights with Environment Map Lights (#17482)
# Objective Transparently uses simple `EnvironmentMapLight`s to mimic `AmbientLight`s. Implements the first part of #17468, but I can implement hemispherical lights in this PR too if needed. ## Solution - A function `EnvironmentMapLight::solid_color(&mut Assets<Image>, Color)` is provided to make an environment light with a solid color. - A new system is added to `SimulationLightSystems` that maps `AmbientLight`s on views or the world to a corresponding `EnvironmentMapLight`. I have never worked with (or on) Bevy before, so nitpicky comments on how I did things are appreciated :). ## Testing Testing was done on a modified version of the `3d/lighting` example, where I removed all lights except the ambient light. I have not included the example, but can if required. ## Migration `bevy_pbr::AmbientLight` has been deprecated, so all usages of it should be replaced by a `bevy_pbr::EnvironmentMapLight` created with `EnvironmentMapLight::solid_color` placed on the camera. There is no alternative to ambient lights as resources. |
||
![]() |
ccb7069e7f
|
Change ChildOf to Childof { parent: Entity} and support deriving Relationship and RelationshipTarget with named structs (#17905)
# Objective fixes #17896 ## Solution Change ChildOf ( Entity ) to ChildOf { parent: Entity } by doing this we also allow users to use named structs for relationship derives, When you have more than 1 field in a struct with named fields the macro will look for a field with the attribute #[relationship] and all of the other fields should implement the Default trait. Unnamed fields are still supported. When u have a unnamed struct with more than one field the macro will fail. Do we want to support something like this ? ```rust #[derive(Component)] #[relationship_target(relationship = ChildOf)] pub struct Children (#[relationship] Entity, u8); ``` I could add this, it but doesn't seem nice. ## Testing crates/bevy_ecs - cargo test ## Showcase ```rust use bevy_ecs::component::Component; use bevy_ecs::entity::Entity; #[derive(Component)] #[relationship(relationship_target = Children)] pub struct ChildOf { #[relationship] pub parent: Entity, internal: u8, }; #[derive(Component)] #[relationship_target(relationship = ChildOf)] pub struct Children { children: Vec<Entity> }; ``` --------- Co-authored-by: Tim Overbeek <oorbecktim@Tims-MacBook-Pro.local> Co-authored-by: Tim Overbeek <oorbecktim@c-001-001-042.client.nl.eduvpn.org> Co-authored-by: Tim Overbeek <oorbecktim@c-001-001-059.client.nl.eduvpn.org> Co-authored-by: Tim Overbeek <oorbecktim@c-001-001-054.client.nl.eduvpn.org> Co-authored-by: Tim Overbeek <oorbecktim@c-001-001-027.client.nl.eduvpn.org> |
||
![]() |
5241e09671
|
Upgrade to Rust Edition 2024 (#17967)
# Objective - Fixes #17960 ## Solution - Followed the [edition upgrade guide](https://doc.rust-lang.org/edition-guide/editions/transitioning-an-existing-project-to-a-new-edition.html) ## Testing - CI --- ## Summary of Changes ### Documentation Indentation When using lists in documentation, proper indentation is now linted for. This means subsequent lines within the same list item must start at the same indentation level as the item. ```rust /* Valid */ /// - Item 1 /// Run-on sentence. /// - Item 2 struct Foo; /* Invalid */ /// - Item 1 /// Run-on sentence. /// - Item 2 struct Foo; ``` ### Implicit `!` to `()` Conversion `!` (the never return type, returned by `panic!`, etc.) no longer implicitly converts to `()`. This is particularly painful for systems with `todo!` or `panic!` statements, as they will no longer be functions returning `()` (or `Result<()>`), making them invalid systems for functions like `add_systems`. The ideal fix would be to accept functions returning `!` (or rather, _not_ returning), but this is blocked on the [stabilisation of the `!` type itself](https://doc.rust-lang.org/std/primitive.never.html), which is not done. The "simple" fix would be to add an explicit `-> ()` to system signatures (e.g., `|| { todo!() }` becomes `|| -> () { todo!() }`). However, this is _also_ banned, as there is an existing lint which (IMO, incorrectly) marks this as an unnecessary annotation. So, the "fix" (read: workaround) is to put these kinds of `|| -> ! { ... }` closuers into variables and give the variable an explicit type (e.g., `fn()`). ```rust // Valid let system: fn() = || todo!("Not implemented yet!"); app.add_systems(..., system); // Invalid app.add_systems(..., || todo!("Not implemented yet!")); ``` ### Temporary Variable Lifetimes The order in which temporary variables are dropped has changed. The simple fix here is _usually_ to just assign temporaries to a named variable before use. ### `gen` is a keyword We can no longer use the name `gen` as it is reserved for a future generator syntax. This involved replacing uses of the name `gen` with `r#gen` (the raw-identifier syntax). ### Formatting has changed Use statements have had the order of imports changed, causing a substantial +/-3,000 diff when applied. For now, I have opted-out of this change by amending `rustfmt.toml` ```toml style_edition = "2021" ``` This preserves the original formatting for now, reducing the size of this PR. It would be a simple followup to update this to 2024 and run `cargo fmt`. ### New `use<>` Opt-Out Syntax Lifetimes are now implicitly included in RPIT types. There was a handful of instances where it needed to be added to satisfy the borrow checker, but there may be more cases where it _should_ be added to avoid breakages in user code. ### `MyUnitStruct { .. }` is an invalid pattern Previously, you could match against unit structs (and unit enum variants) with a `{ .. }` destructuring. This is no longer valid. ### Pretty much every use of `ref` and `mut` are gone Pattern binding has changed to the point where these terms are largely unused now. They still serve a purpose, but it is far more niche now. ### `iter::repeat(...).take(...)` is bad New lint recommends using the more explicit `iter::repeat_n(..., ...)` instead. ## Migration Guide The lifetimes of functions using return-position impl-trait (RPIT) are likely _more_ conservative than they had been previously. If you encounter lifetime issues with such a function, please create an issue to investigate the addition of `+ use<...>`. ## Notes - Check the individual commits for a clearer breakdown for what _actually_ changed. --------- Co-authored-by: François Mockers <francois.mockers@vleue.com> |
||
![]() |
aa8793f6b4
|
Add ways to configure EasingFunction::Steps via new StepConfig (#17752)
# Objective - In #17743, attention was raised to the fact that we supported an unusual kind of step easing function. The author of the fix kindly provided some links to standards used in CSS. It would be desirable to support generally agreed upon standards so this PR here tries to implement an extra configuration option of the step easing function - Resolve #17744 ## Solution - Introduce `StepConfig` - `StepConfig` can configure both the number of steps and the jumping behavior of the function - `StepConfig` replaces the raw `usize` parameter of the `EasingFunction::Steps(usize)` construct. - `StepConfig`s default jumping behavior is `end`, so in that way it follows #17743 ## Testing - I added a new test per `JumpAt` jumping behavior. These tests replicate the visuals that can be found at https://developer.mozilla.org/en-US/docs/Web/CSS/easing-function/steps#description ## Migration Guide - `EasingFunction::Steps` now uses a `StepConfig` instead of a raw `usize`. You can replicate the previous behavior by replaceing `EasingFunction::Steps(10)` with `EasingFunction::Steps(StepConfig::new(10))`. --------- Co-authored-by: François Mockers <francois.mockers@vleue.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
a9de5164a8
|
Tidy up easing_functions example (#17742)
# Objective After #17461, the ease function labels in this example are a bit cramped, especially in the bottom row. This adjusts the spacing slightly and centers the labels. ## Solution - The label is now a child of the plot and they are drawn around the center of the transform - Plot size and extents are now constants, and this thing has been banished: ```rust i as f32 * 95.0 - 1280.0 / 2.0 + 25.0, -100.0 - ((j as f32 * 250.0) - 300.0), 0.0, ``` - There's room for expansion in another row, so make that easier by doing the chunking by row - Other misc tidying of variable names, sprinkled in a few comments, etc. ## Before <img width="1280" alt="Screenshot 2025-02-08 at 7 33 14 AM" src="https://github.com/user-attachments/assets/0b79c619-d295-4ab1-8cd1-d23c862d06c5" /> ## After <img width="1280" alt="Screenshot 2025-02-08 at 7 32 45 AM" src="https://github.com/user-attachments/assets/656ef695-9aa8-42e9-b867-1718294316bd" /> |
||
![]() |
7c2d54c93f
|
EaseFunction svg graphs in doc (#17461)
# Objective The docs of `EaseFunction` don't visualize the different functions, requiring you to check out the Bevy repo and running the `easing_function` example. ## Solution - Add tool to generate suitable svg graphs. This only needs to be re-run when adding new ease functions. - works with all themes - also add missing easing functions to example. --- ## Showcase  --------- Co-authored-by: François Mockers <mockersf@gmail.com> |
||
![]() |
ba5e71f53d
|
Parent -> ChildOf (#17427)
Fixes #17412 ## Objective `Parent` uses the "has a X" naming convention. There is increasing sentiment that we should use the "is a X" naming convention for relationships (following #17398). This leaves `Children` as-is because there is prevailing sentiment that `Children` is clearer than `ParentOf` in many cases (especially when treating it like a collection). This renames `Parent` to `ChildOf`. This is just the implementation PR. To discuss the path forward, do so in #17412. ## Migration Guide - The `Parent` component has been renamed to `ChildOf`. |
||
![]() |
b2f3248432
|
Make the animated_mesh example more intuitive (#17421)
# Objective Make the `animated_mesh` example more intuitive and easier for the user to extend. # Solution The `animated_mesh` example shows how to spawn a single mesh and play a single animation. The original code is roughly: 1. In `setup_mesh_and_animation`, spawn an entity with a SceneRoot that will load and spawn the mesh. Also record the animation to play as a resource. 2. Use `play_animation_once_loaded` to detect when any animation players are spawned, then play the animation from the resource. When I used this example as a starting point for my own app, I hit a wall when trying to spawn multiple meshes with different animations. `play_animation_once_loaded` tells me an animation player spawned somewhere, but how do I get from there to the right animation? The entity it runs on is spawned by the scene so I can't attach any data to it? The new code takes a different approach. Instead of a global resource, the animation is recorded as a component on the entity with the SceneRoot. Instead of detecting animation players spawning wherever, an observer is attached to that specific entity. This feels more intuitive and localised, and I think most users will work out how to get from there to different animations and meshes. The downside is more lines of code, and the "find the animation players" part still feels a bit magical and inefficient. # Side Notes - The solution was mostly stolen from https://github.com/bevyengine/bevy/issues/14852#issuecomment-2481401769. - The example still feels too complicated. - "Why do I have to make this graph to play one animation?" - "Why can't I choose and play the animation in one step and avoid this temporary component?" - I think this requires engine changes. - I originally started on a separate example of multiple meshes ([branch](https://github.com/bevyengine/bevy/compare/main...greeble-dev:bevy:animated-mesh-multiple)). - I decided that the user could probably work this out themselves from the single animation example. - But maybe still worth following through. # Testing `cargo run --example animated_mesh` --------- Co-authored-by: Rob Parrett <robparrett@gmail.com> |
||
![]() |
21f1e3045c
|
Relationships (non-fragmenting, one-to-many) (#17398)
This adds support for one-to-many non-fragmenting relationships (with planned paths for fragmenting and non-fragmenting many-to-many relationships). "Non-fragmenting" means that entities with the same relationship type, but different relationship targets, are not forced into separate tables (which would cause "table fragmentation"). Functionally, this fills a similar niche as the current Parent/Children system. The biggest differences are: 1. Relationships have simpler internals and significantly improved performance and UX. Commands and specialized APIs are no longer necessary to keep everything in sync. Just spawn entities with the relationship components you want and everything "just works". 2. Relationships are generalized. Bevy can provide additional built in relationships, and users can define their own. **REQUEST TO REVIEWERS**: _please don't leave top level comments and instead comment on specific lines of code. That way we can take advantage of threaded discussions. Also dont leave comments simply pointing out CI failures as I can read those just fine._ ## Built on top of what we have Relationships are implemented on top of the Bevy ECS features we already have: components, immutability, and hooks. This makes them immediately compatible with all of our existing (and future) APIs for querying, spawning, removing, scenes, reflection, etc. The fewer specialized APIs we need to build, maintain, and teach, the better. ## Why focus on one-to-many non-fragmenting first? 1. This allows us to improve Parent/Children relationships immediately, in a way that is reasonably uncontroversial. Switching our hierarchy to fragmenting relationships would have significant performance implications. ~~Flecs is heavily considering a switch to non-fragmenting relations after careful considerations of the performance tradeoffs.~~ _(Correction from @SanderMertens: Flecs is implementing non-fragmenting storage specialized for asset hierarchies, where asset hierarchies are many instances of small trees that have a well defined structure)_ 2. Adding generalized one-to-many relationships is currently a priority for the [Next Generation Scene / UI effort](https://github.com/bevyengine/bevy/discussions/14437). Specifically, we're interested in building reactions and observers on top. ## The changes This PR does the following: 1. Adds a generic one-to-many Relationship system 3. Ports the existing Parent/Children system to Relationships, which now lives in `bevy_ecs::hierarchy`. The old `bevy_hierarchy` crate has been removed. 4. Adds on_despawn component hooks 5. Relationships can opt-in to "despawn descendants" behavior, meaning that the entire relationship hierarchy is despawned when `entity.despawn()` is called. The built in Parent/Children hierarchies enable this behavior, and `entity.despawn_recursive()` has been removed. 6. `world.spawn` now applies commands after spawning. This ensures that relationship bookkeeping happens immediately and removes the need to manually flush. This is in line with the equivalent behaviors recently added to the other APIs (ex: insert). 7. Removes the ValidParentCheckPlugin (system-driven / poll based) in favor of a `validate_parent_has_component` hook. ## Using Relationships The `Relationship` trait looks like this: ```rust pub trait Relationship: Component + Sized { type RelationshipSources: RelationshipSources<Relationship = Self>; fn get(&self) -> Entity; fn from(entity: Entity) -> Self; } ``` A relationship is a component that: 1. Is a simple wrapper over a "target" Entity. 2. Has a corresponding `RelationshipSources` component, which is a simple wrapper over a collection of entities. Every "target entity" targeted by a "source entity" with a `Relationship` has a `RelationshipSources` component, which contains every "source entity" that targets it. For example, the `Parent` component (as it currently exists in Bevy) is the `Relationship` component and the entity containing the Parent is the "source entity". The entity _inside_ the `Parent(Entity)` component is the "target entity". And that target entity has a `Children` component (which implements `RelationshipSources`). In practice, the Parent/Children relationship looks like this: ```rust #[derive(Relationship)] #[relationship(relationship_sources = Children)] pub struct Parent(pub Entity); #[derive(RelationshipSources)] #[relationship_sources(relationship = Parent)] pub struct Children(Vec<Entity>); ``` The Relationship and RelationshipSources derives automatically implement Component with the relevant configuration (namely, the hooks necessary to keep everything in sync). The most direct way to add relationships is to spawn entities with relationship components: ```rust let a = world.spawn_empty().id(); let b = world.spawn(Parent(a)).id(); assert_eq!(world.entity(a).get::<Children>().unwrap(), &[b]); ``` There are also convenience APIs for spawning more than one entity with the same relationship: ```rust world.spawn_empty().with_related::<Children>(|s| { s.spawn_empty(); s.spawn_empty(); }) ``` The existing `with_children` API is now a simpler wrapper over `with_related`. This makes this change largely non-breaking for existing spawn patterns. ```rust world.spawn_empty().with_children(|s| { s.spawn_empty(); s.spawn_empty(); }) ``` There are also other relationship APIs, such as `add_related` and `despawn_related`. ## Automatic recursive despawn via the new on_despawn hook `RelationshipSources` can opt-in to "despawn descendants" behavior, which will despawn all related entities in the relationship hierarchy: ```rust #[derive(RelationshipSources)] #[relationship_sources(relationship = Parent, despawn_descendants)] pub struct Children(Vec<Entity>); ``` This means that `entity.despawn_recursive()` is no longer required. Instead, just use `entity.despawn()` and the relevant related entities will also be despawned. To despawn an entity _without_ despawning its parent/child descendants, you should remove the `Children` component first, which will also remove the related `Parent` components: ```rust entity .remove::<Children>() .despawn() ``` This builds on the on_despawn hook introduced in this PR, which is fired when an entity is despawned (before other hooks). ## Relationships are the source of truth `Relationship` is the _single_ source of truth component. `RelationshipSources` is merely a reflection of what all the `Relationship` components say. By embracing this, we are able to significantly improve the performance of the system as a whole. We can rely on component lifecycles to protect us against duplicates, rather than needing to scan at runtime to ensure entities don't already exist (which results in quadratic runtime). A single source of truth gives us constant-time inserts. This does mean that we cannot directly spawn populated `Children` components (or directly add or remove entities from those components). I personally think this is a worthwhile tradeoff, both because it makes the performance much better _and_ because it means theres exactly one way to do things (which is a philosophy we try to employ for Bevy APIs). As an aside: treating both sides of the relationship as "equivalent source of truth relations" does enable building simple and flexible many-to-many relationships. But this introduces an _inherent_ need to scan (or hash) to protect against duplicates. [`evergreen_relations`](https://github.com/EvergreenNest/evergreen_relations) has a very nice implementation of the "symmetrical many-to-many" approach. Unfortunately I think the performance issues inherent to that approach make it a poor choice for Bevy's default relationship system. ## Followup Work * Discuss renaming `Parent` to `ChildOf`. I refrained from doing that in this PR to keep the diff reasonable, but I'm personally biased toward this change (and using that naming pattern generally for relationships). * [Improved spawning ergonomics](https://github.com/bevyengine/bevy/discussions/16920) * Consider adding relationship observers/triggers for "relationship targets" whenever a source is added or removed. This would replace the current "hierarchy events" system, which is unused upstream but may have existing users downstream. I think triggers are the better fit for this than a buffered event queue, and would prefer not to add that back. * Fragmenting relations: My current idea hinges on the introduction of "value components" (aka: components whose type _and_ value determines their ComponentId, via something like Hashing / PartialEq). By labeling a Relationship component such as `ChildOf(Entity)` as a "value component", `ChildOf(e1)` and `ChildOf(e2)` would be considered "different components". This makes the transition between fragmenting and non-fragmenting a single flag, and everything else continues to work as expected. * Many-to-many support * Non-fragmenting: We can expand Relationship to be a list of entities instead of a single entity. I have largely already written the code for this. * Fragmenting: With the "value component" impl mentioned above, we get many-to-many support "for free", as it would allow inserting multiple copies of a Relationship component with different target entities. Fixes #3742 (If this PR is merged, I think we should open more targeted followup issues for the work above, with a fresh tracking issue free of the large amount of less-directed historical context) Fixes #17301 Fixes #12235 Fixes #15299 Fixes #15308 ## Migration Guide * Replace `ChildBuilder` with `ChildSpawnerCommands`. * Replace calls to `.set_parent(parent_id)` with `.insert(Parent(parent_id))`. * Replace calls to `.replace_children()` with `.remove::<Children>()` followed by `.add_children()`. Note that you'll need to manually despawn any children that are not carried over. * Replace calls to `.despawn_recursive()` with `.despawn()`. * Replace calls to `.despawn_descendants()` with `.despawn_related::<Children>()`. * If you have any calls to `.despawn()` which depend on the children being preserved, you'll need to remove the `Children` component first. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
c96949dabe
|
Improve the animated_mesh example (#17328)
# Objective Building upon https://github.com/bevyengine/bevy/pull/17191, improve the `animated_mesh` example by removing code, adding comments, and making the example more c&p'able. ## Solution - Split the setup function in two to clarify what the example is demonstrating. - `setup_mesh_and_animation` is the demonstration. - `setup_camera_and_environment` just sets up the example app. - Changed the animation playing to use `AnimationPlayer` directly instead of creating `AnimationTransitions`. - This appears sufficient when only playing a single animation. - Added a comment pointing users to an example of multiple animations. - Changed the animation to be the run cycle. - I think it got accidentally changed to the idle in [#17191](https://github.com/bevyengine/bevy/pull/17191), so this is reverting back to the original. - Note that we can improve it to select the animation by name if [#16529](https://github.com/bevyengine/bevy/pull/16529) lands. - Renamed `FOX_PATH` to a more neutral `GLTF_PATH`. - Updated the example descriptions to mention the fox. - This adds a little character and hints that the example involves character animation. - Removed a seemingly redundant `AnimationGraphHandle` component. - Removed an unnecessary `clone()`. - Added various comments. ## Notes - A draft of this PR was discussed on Discord: https://discord.com/channels/691052431525675048/1326910663972618302/1326920498663133348 - There was discord discussion on whether a component is "inserted onto", "inserted into" or "added to" an entity. - "Added to" is most common in code and docs, and seems best to me. But it awkwardly differs from the name of `EntityCommands::insert`. - This PR prefers "added to". - I plan to follow up this PR with similar changes to the `animated_mesh_control` and `animated_mesh_events` examples. - But I could roll them into this PR if requested. ## Testing `cargo run --example animated_mesh` --------- Co-authored-by: François Mockers <mockersf@gmail.com> |
||
![]() |
b77e3ef33a
|
Fix a few typos (#17292)
# Objective Stumbled upon a `from <-> form` transposition while reviewing a PR, thought it was interesting, and went down a bit of a rabbit hole. ## Solution Fix em |
||
![]() |
6462935b32
|
Rename animated fox examples to better communicate their purpose (#17239)
Fixes #17192. Replaces "animated_fox" with "animated_mesh". I considered a few different names - should it say "skinned_mesh" to be precise? Should it mention gltf? But "animated_mesh" seems intuitive and keeps it short. ## Testing - Ran all three examples (Windows 10). |
||
![]() |
6f68776eac
|
Split up animated_fox example (#17191)
# Objective Our `animated_fox` example used to be a bare-bones example of how to spawn an animated gltf and play a single animation. I think that's a valuable example, and the current `animated_fox` example is doing way too much. Users who are trying to understand how our animation system are presented with an enormous amount of information that may not be immediately relevant. Over the past few releases, I've been migrating a simple app of mine where the only animation I need is a single gltf that starts playing a single animation when it is loaded. It has been a slight struggle to wade through changes to the animation system to figure out the minimal amount of things required to accomplish this. Somewhat motivated by this [recent reddit thread](https://www.reddit.com/r/rust/comments/1ht93vl/comment/m5c0nc9/?utm_source=share&utm_medium=mweb3x&utm_name=mweb3xcss&utm_term=1) where Bevy and animation got a mention. ## Solution - Split `animated_fox` into three separate examples - `animated_fox` - Loads and immediately plays a single animation - `animated_fox_control` - Shows how to control animations - `animated_fox_events` - Shows fancy particles when the fox's feet hit the ground - Some minor drive-by tidying of these examples I have created this PR after playing around with the idea and liking how it turned out, but the duplication isn't totally ideal and there's some slight overlap with other examples and inconsistencies: - `animation_events` is simplified and not specific to "loaded animated scenes" and seems valuable on its own - `animation_graph` also uses a fox I am happy to close this if there's no consensus that it's a good idea / step forward for these examples. ## Testing `cargo run --example animated_fox` `cargo run --example animated_fox_control` `cargo run --example animated_fox_events` |
||
![]() |
bf3692a011
|
Introduce support for mixed lighting by allowing lights to opt out of contributing diffuse light to lightmapped objects. (#16761)
This PR adds support for *mixed lighting* to Bevy, whereby some parts of the scene are lightmapped, while others take part in real-time lighting. (Here *real-time lighting* means lighting at runtime via the PBR shader, as opposed to precomputed light using lightmaps.) It does so by adding a new field, `affects_lightmapped_meshes` to `IrradianceVolume` and `AmbientLight`, and a corresponding field `affects_lightmapped_mesh_diffuse` to `DirectionalLight`, `PointLight`, `SpotLight`, and `EnvironmentMapLight`. By default, this value is set to true; when set to false, the light contributes nothing to the diffuse irradiance component to meshes with lightmaps. Note that specular light is unaffected. This is because the correct way to bake specular lighting is *directional lightmaps*, which we have no support for yet. There are two general ways I expect this field to be used: 1. When diffuse indirect light is baked into lightmaps, irradiance volumes and reflection probes shouldn't contribute any diffuse light to the static geometry that has a lightmap. That's because the baking tool should have already accounted for it, and in a higher-quality fashion, as lightmaps typically offer a higher effective texture resolution than the light probe does. 2. When direct diffuse light is baked into a lightmap, punctual lights shouldn't contribute any diffuse light to static geometry with a lightmap, to avoid double-counting. It may seem odd to bake *direct* light into a lightmap, as opposed to indirect light. But there is a use case: in a scene with many lights, avoiding light leaks requires shadow mapping, which quickly becomes prohibitive when many lights are involved. Baking lightmaps allows light leaks to be eliminated on static geometry. A new example, `mixed_lighting`, has been added. It demonstrates a sofa (model from the [glTF Sample Assets]) that has been lightmapped offline using [Bakery]. It has four modes: 1. In *baked* mode, all objects are locked in place, and all the diffuse direct and indirect light has been calculated ahead of time. Note that the bottom of the sphere has a red tint from the sofa, illustrating that the baking tool captured indirect light for it. 2. In *mixed direct* mode, lightmaps capturing diffuse direct and indirect light have been pre-calculated for the static objects, but the dynamic sphere has real-time lighting. Note that, because the diffuse lighting has been entirely pre-calculated for the scenery, the dynamic sphere casts no shadow. In a real app, you would typically use real-time lighting for the most important light so that dynamic objects can shadow the scenery and relegate baked lighting to the less important lights for which shadows aren't as important. Also note that there is no red tint on the sphere, because there is no global illumination applied to it. In an actual game, you could fix this problem by supplementing the lightmapped objects with an irradiance volume. 3. In *mixed indirect* mode, all direct light is calculated in real-time, and the static objects have pre-calculated indirect lighting. This corresponds to the mode that most applications are expected to use. Because direct light on the scenery is computed dynamically, shadows are fully supported. As in mixed direct mode, there is no global illumination on the sphere; in a real application, irradiance volumes could be used to supplement the lightmaps. 4. In *real-time* mode, no lightmaps are used at all, and all punctual lights are rendered in real-time. No global illumination exists. In the example, you can click around to move the sphere, unless you're in baked mode, in which case the sphere must be locked in place to be lit correctly. ## Showcase Baked mode:  Mixed direct mode:  Mixed indirect mode (default):  Real-time mode:  ## Migration guide * The `AmbientLight` resource, the `IrradianceVolume` component, and the `EnvironmentMapLight` component now have `affects_lightmapped_meshes` fields. If you don't need to use that field (for example, if you aren't using lightmaps), you can safely set the field to true. * `DirectionalLight`, `PointLight`, and `SpotLight` now have `affects_lightmapped_mesh_diffuse` fields. If you don't need to use that field (for example, if you aren't using lightmaps), you can safely set the field to true. [glTF Sample Assets]: https://github.com/KhronosGroup/glTF-Sample-Assets/tree/main [Bakery]: https://geom.io/bakery/wiki/index.php?title=Bakery_-_GPU_Lightmapper |
||
![]() |
711246aa34
|
Update hashbrown to 0.15 (#15801)
Updating dependencies; adopted version of #15696. (Supercedes #15696.) Long answer: hashbrown is no longer using ahash by default, meaning that we can't use the default-hasher methods with ahasher. So, we have to use the longer-winded versions instead. This takes the opportunity to also switch our default hasher as well, but without actually enabling the default-hasher feature for hashbrown, meaning that we'll be able to change our hasher more easily at the cost of all of these method calls being obnoxious forever. One large change from 0.15 is that `insert_unique_unchecked` is now `unsafe`, and for cases where unsafe code was denied at the crate level, I replaced it with `insert`. ## Migration Guide `bevy_utils` has updated its version of `hashbrown` to 0.15 and now defaults to `foldhash` instead of `ahash`. This means that if you've hard-coded your hasher to `bevy_utils::AHasher` or separately used the `ahash` crate in your code, you may need to switch to `foldhash` to ensure that everything works like it does in Bevy. |
||
![]() |
61b98ec80f
|
Rename trigger.entity() to trigger.target() (#16716)
# Objective - A `Trigger` has multiple associated `Entity`s - the entity observing the event, and the entity that was targeted by the event. - The field `entity: Entity` encodes no semantic information about what the entity is used for, you can already tell that it's an `Entity` by the type signature! ## Solution - Rename `trigger.entity()` to `trigger.target()` --- ## Changelog - `Trigger`s are associated with multiple entities. `Trigger::entity()` has been renamed to `Trigger::target()` to reflect the semantics of the entity being returned. ## Migration Guide - Rename `Trigger::entity()` to `Trigger::target()`. - Rename `ObserverTrigger::entity` to `ObserverTrigger::target` |
||
![]() |
0707c0717b
|
✏️ Fix typos across bevy (#16702)
# Objective Fixes typos in bevy project, following suggestion in https://github.com/bevyengine/bevy-website/pull/1912#pullrequestreview-2483499337 ## Solution I used https://github.com/crate-ci/typos to find them. I included only the ones that feel undebatable too me, but I am not in game engine so maybe some terms are expected. I left out the following typos: - `reparametrize` => `reparameterize`: There are a lot of occurences, I believe this was expected - `semicircles` => `hemicircles`: 2 occurences, may mean something specific in geometry - `invertation` => `inversion`: may mean something specific - `unparented` => `parentless`: may mean something specific - `metalness` => `metallicity`: may mean something specific ## Testing - Did you test these changes? If so, how? I did not test the changes, most changes are related to raw text. I expect the others to be tested by the CI. - Are there any parts that need more testing? I do not think - How can other people (reviewers) test your changes? Is there anything specific they need to know? To me there is nothing to test - If relevant, what platforms did you test these changes on, and are there any important ones you can't test? --- ## Migration Guide > This section is optional. If there are no breaking changes, you can delete this section. (kept in case I include the `reparameterize` change here) - If this PR is a breaking change (relative to the last release of Bevy), describe how a user might need to migrate their code to support these changes - Simply adding new functionality is not a breaking change. - Fixing behavior that was definitely a bug, rather than a questionable design choice is not a breaking change. ## Questions - [x] Should I include the above typos? No (https://github.com/bevyengine/bevy/pull/16702#issuecomment-2525271152) - [ ] Should I add `typos` to the CI? (I will check how to configure it properly) This project looks awesome, I really enjoy reading the progress made, thanks to everyone involved. |
||
![]() |
af10aa38aa
|
AnimatedField and Rework Evaluators (#16484)
# Objective Animating component fields requires too much boilerplate at the moment: ```rust #[derive(Reflect)] struct FontSizeProperty; impl AnimatableProperty for FontSizeProperty { type Component = TextFont; type Property = f32; fn get_mut(component: &mut Self::Component) -> Option<&mut Self::Property> { Some(&mut component.font_size) } } animation_clip.add_curve_to_target( animation_target_id, AnimatableKeyframeCurve::new( [0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0] .into_iter() .zip([24.0, 80.0, 24.0, 80.0, 24.0, 80.0, 24.0]), ) .map(AnimatableCurve::<FontSizeProperty, _>::from_curve) .expect("should be able to build translation curve because we pass in valid samples"), ); ``` ## Solution This adds `AnimatedField` and an `animated_field!` macro, enabling the following: ```rust animation_clip.add_curve_to_target( animation_target_id, AnimatableCurve::new( animated_field!(TextFont::font_size), AnimatableKeyframeCurve::new( [0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0] .into_iter() .zip([24.0, 80.0, 24.0, 80.0, 24.0, 80.0, 24.0]), ) .expect( "should be able to build translation curve because we pass in valid samples", ), ), ); ``` This required reworking the internals a bit, namely stripping out a lot of the `Reflect` usage, as that implementation was fundamentally incompatible with the `AnimatedField` pattern. `Reflect` was being used in this context just to downcast traits. But we can get downcasting behavior without the `Reflect` requirement by implementing `Downcast` for `AnimationCurveEvaluator`. This also reworks "evaluator identity" to support either a (Component / Field) pair, or a TypeId. This allows properties to reuse evaluators, even if they have different accessor methods. The "contract" here is that for a given (Component / Field) pair, the accessor will return the same value. Fields are identified by their Reflect-ed field index. The (TypeId, usize) is prehashed and cached to optimize for lookup speed. This removes the built-in hard-coded TranslationCurve / RotationCurve / ScaleCurve in favor of AnimatableField. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
a9a4b069b6
|
Make some examples deterministic (#16488)
# Objective - Improve reproducibility of examples ## Solution - Use seeded rng when needed - Use fixed z-ordering when needed ## Testing ```sh steps=5; echo "cpu_draw\nparallel_query\nanimated_fox\ntransparency_2d" > test cargo run -p example-showcase -- run --stop-frame 250 --screenshot-frame 100 --fixed-frame-time 0.05 --example-list test --in-ci; mv screenshots base; for prefix in `seq 0 $steps`; do echo step $prefix; cargo run -p example-showcase -- run --stop-frame 250 --screenshot-frame 100 --fixed-frame-time 0.05 --example-list test; mv screenshots $prefix-screenshots; done; mv base screenshots for prefix in `seq 0 $steps`; do echo check $prefix for file in screenshots/*/*; do echo $file; diff $file $prefix-$file; done; done; ``` |
||
![]() |
513be52505
|
AnimationEvent -> Event and other improvements (#16440)
# Objective Needing to derive `AnimationEvent` for `Event` is unnecessary, and the trigger logic coupled to it feels like we're coupling "event producer" logic with the event itself, which feels wrong. It also comes with a bunch of complexity, which is again unnecessary. We can have the flexibility of "custom animation event trigger logic" without this coupling and complexity. The current `animation_events` example is also needlessly complicated, due to it needing to work around system ordering issues. The docs describing it are also slightly wrong. We can make this all a non-issue by solving the underlying ordering problem. Related to this, we use the `bevy_animation::Animation` system set to solve PostUpdate animation order-of-operations issues. If we move this to bevy_app as part of our "core schedule", we can cut out needless `bevy_animation` crate dependencies in these instances. ## Solution - Remove `AnimationEvent`, the derive, and all other infrastructure associated with it (such as the `bevy_animation/derive` crate) - Replace all instances of `AnimationEvent` traits with `Event + Clone` - Store and use functions for custom animation trigger logic (ex: `clip.add_event_fn()`). For "normal" cases users dont need to think about this and should use the simpler `clip.add_event()` - Run the `Animation` system set _before_ updating text - Move `bevy_animation::Animation` to `bevy_app::Animation`. Remove unnecessary `bevy_animation` dependency from `bevy_ui` - Adjust `animation_events` example to use the simpler `clip.add_event` API, as the workarounds are no longer necessary This is polishing work that will land in 0.15, and I think it is simple enough and valuable enough to land in 0.15 with it, in the interest of making the feature as compelling as possible. |
||
![]() |
7477928f13
|
Use normal constructors for EasingCurve, FunctionCurve, ConstantCurve (#16367)
# Objective We currently use special "floating" constructors for `EasingCurve`, `FunctionCurve`, and `ConstantCurve` (ex: `easing_curve`). This erases the type being created (and in general "what is happening" structurally), for very minimal ergonomics improvements. With rare exceptions, we prefer normal `X::new()` constructors over floating `x()` constructors in Bevy. I don't think this use case merits special casing here. ## Solution Add `EasingCurve::new()`, use normal constructors everywhere, and remove the floating constructors. I think this should land in 0.15 in the interest of not breaking people later. |
||
![]() |
9beb1d96e7
|
Incorporate all node weights in additive blending (#16279)
# Objective In the existing implementation, additive blending effectively treats the node with least index specially by basically forcing its weight to be `1.0` regardless of what its computed weight would be (based on the weights in the `AnimationGraph` and `AnimationPlayer`). Arguably this makes some amount of sense, because the "base" animation is often one which was not authored to be used additively, meaning that its sampled values are interpreted absolutely rather than as deltas. However, this also leads to strange behavior with respect to animation masks: if the "base" animation is masked out on some target, then the next node is treated as the "base" animation, despite the fact that it would normally be interpreted additively, and the weight of that animation is thrown away as a result. This is all kind of weird and revolves around special treatment (if the behavior is even really intentional in the first place). From a mathematical standpoint, there is nothing special about how the "base" animation must be treated other than having a weight of 1.0 under an `Add` node, which is something that the user can do without relying on some bizarre corner-case behavior of the animation system — this is the only present situation under which weights are discarded. This PR changes this behavior so that the weight of every node is incorporated. In other words, for an animation graph that looks like this: ```text ┌───────────────┐ │Base clip ┼──┐ │ 0.5 │ │ └───────────────┘ │ ┌───────────────┐ │ ┌───────────────┐ ┌────┐ │Additive clip 1┼──┼─►┤Additive blend ┼────►│Root│ │ 0.1 │ │ │ 1.0 │ └────┘ └───────────────┘ │ └───────────────┘ ┌───────────────┐ │ │Additive clip 2┼──┘ │ 0.2 │ └───────────────┘ ``` Previously, the result would have been ```text base_clip + 0.1 * additive_clip_1 + 0.2 * additive_clip_2 ``` whereas now it would be ```text 0.5 * base_clip + 0.1 * additive_clip_1 + 0.2 * additive_clip_2 ``` and in the scenario where `base_clip` is masked out: ```text additive_clip_1 + 0.2 * additive_clip_2 ``` vs. ```text 0.1 * additive_clip_1 + 0.2 * additive_clip_2 ``` ## Solution For background, the way that the additive blending procedure works is something like this: - During graph traversal, the node values and weights of the children are pushed onto the evaluator `stack`. The traversal order guarantees that the item with least node index will be on top. - Once we reach the `Add` node itself, we start popping off the `stack` and into the evaluator's `blend_register`, which is an accumulator holding up to one weight-value pair: - If the `blend_register` is empty, it is filled using data from the top of the `stack`. - Otherwise, the `blend_register` is combined with data popped from the `stack` and updated. In the example above, the additive blending steps would look like this (with the pre-existing implementation): 1. The `blend_register` is empty, so we pop `(base_clip, 0.5)` from the top of the `stack` and put it in. Now the value of the `blend_register` is `(base_clip, 0.5)`. 2. The `blend_register` is non-empty: we pop `(additive_clip_1, 0.1)` from the top of the `stack` and combine it additively with the value in the `blend_register`, forming `(base_clip + 0.1 * additive_clip_1, 0.6)` in the `blend_register` (the carried weight value goes unused). 3. The `blend_register` is non-empty: we pop `(additive_clip_2, 0.2)` from the top of the `stack` and combine it additively with the value in the `blend_register`, forming `(base_clip + 0.1 * additive_clip_1 + 0.2 * additive_clip_2, 0.8)` in the `blend_register`. The solution in this PR changes step 1: the `base_clip` is multiplied by its weight as it is added to the `blend_register` in the first place, yielding `0.5 * base_clip + 0.1 * additive_clip_1 + 0.2 * additive_clip_2` as the final result. ### Note for reviewers It might be tempting to look at the code, which contains a segment that looks like this: ```rust if additive { current_value = A::blend( [ BlendInput { weight: 1.0, // <-- value: current_value, additive: true, }, BlendInput { weight: weight_to_blend, value: value_to_blend, additive: true, }, ] .into_iter(), ); } ``` and conclude that the explicit value of `1.0` is responsible for overwriting the weight of the base animation. This is incorrect. Rather, this additive blend has to be written this way because it is multiplying the *existing value in the blend register* by 1 (i.e. not doing anything) before adding the next value to it. Changing this to another quantity (e.g. the existing weight) would cause the value in the blend register to be spuriously multiplied down. ## Testing Tested on `animation_masks` example. Checked `morph_weights` example as well. ## Migration Guide I will write a migration guide later if this change is not included in 0.15. |
||
![]() |
8b636f32cf
|
Remove the invalid system ordering in the animation example. (#16173)
# Objective In the animation example, there is the code `.add_systems(Update, init_animations.before(animate_targets))`, where `animate_targets` is added to the `PostUpdate` in the `AnimationPlugin`. Therefore, the `.before(animate_targets)` here is ineffective and should be removed. |
||
![]() |
c4c1c8ffa1
|
Undeprecate is_playing_animation (#16121)
# Objective
- Fixes #16098
## Solution
- Undeprecate `is_playing_animation` and copy the docs from
`animation_is_playing` to it.
## Testing
- CI
## Migration
|
||
![]() |
30d84519a2
|
Use en-us locale for typos (#16037)
# Objective Bevy seems to want to standardize on "American English" spellings. Not sure if this is laid out anywhere in writing, but see also #15947. While perusing the docs for `typos`, I noticed that it has a `locale` config option and tried it out. ## Solution Switch to `en-us` locale in the `typos` config and run `typos -w` ## Migration Guide The following methods or fields have been renamed from `*dependants*` to `*dependents*`. - `ProcessorAssetInfo::dependants` - `ProcessorAssetInfos::add_dependant` - `ProcessorAssetInfos::non_existent_dependants` - `AssetInfo::dependants_waiting_on_load` - `AssetInfo::dependants_waiting_on_recursive_dep_load` - `AssetInfos::loader_dependants` - `AssetInfos::remove_dependants_and_labels` |
||
![]() |
c65f2920bb
|
Fix animation_masks example's buttons (#15996)
# Objective Fixes #15995 ## Solution Corrects a mistake made during the example migration in #15591. `AnimationControl` was meant to be on the parent, not the child. So the query in `update_ui` was no longer matching. ## Testing `cargo run --example animation_masks` |
||
![]() |
015f2c69ca
|
Merge Style properties into Node. Use ComputedNode for computed properties. (#15975)
# Objective Continue improving the user experience of our UI Node API in the direction specified by [Bevy's Next Generation Scene / UI System](https://github.com/bevyengine/bevy/discussions/14437) ## Solution As specified in the document above, merge `Style` fields into `Node`, and move "computed Node fields" into `ComputedNode` (I chose this name over something like `ComputedNodeLayout` because it currently contains more than just layout info. If we want to break this up / rename these concepts, lets do that in a separate PR). `Style` has been removed. This accomplishes a number of goals: ## Ergonomics wins Specifying both `Node` and `Style` is now no longer required for non-default styles Before: ```rust commands.spawn(( Node::default(), Style { width: Val::Px(100.), ..default() }, )); ``` After: ```rust commands.spawn(Node { width: Val::Px(100.), ..default() }); ``` ## Conceptual clarity `Style` was never a comprehensive "style sheet". It only defined "core" style properties that all `Nodes` shared. Any "styled property" that couldn't fit that mold had to be in a separate component. A "real" style system would style properties _across_ components (`Node`, `Button`, etc). We have plans to build a true style system (see the doc linked above). By moving the `Style` fields to `Node`, we fully embrace `Node` as the driving concept and remove the "style system" confusion. ## Next Steps * Consider identifying and splitting out "style properties that aren't core to Node". This should not happen for Bevy 0.15. --- ## Migration Guide Move any fields set on `Style` into `Node` and replace all `Style` component usage with `Node`. Before: ```rust commands.spawn(( Node::default(), Style { width: Val::Px(100.), ..default() }, )); ``` After: ```rust commands.spawn(Node { width: Val::Px(100.), ..default() }); ``` For any usage of the "computed node properties" that used to live on `Node`, use `ComputedNode` instead: Before: ```rust fn system(nodes: Query<&Node>) { for node in &nodes { let computed_size = node.size(); } } ``` After: ```rust fn system(computed_nodes: Query<&ComputedNode>) { for computed_node in &computed_nodes { let computed_size = computed_node.size(); } } ``` |
||
![]() |
eb19a9ea0b
|
Migrate UI bundles to required components (#15898)
# Objective - Migrate UI bundles to required components, fixes #15889 ## Solution - deprecate `NodeBundle` in favor of `Node` - deprecate `ImageBundle` in favor of `UiImage` - deprecate `ButtonBundle` in favor of `Button` ## Testing CI. ## Migration Guide - Replace all uses of `NodeBundle` with `Node`. e.g. ```diff commands - .spawn(NodeBundle { - style: Style { + .spawn(( + Node::default(), + Style { width: Val::Percent(100.), align_items: AlignItems::Center, justify_content: JustifyContent::Center, ..default() }, - ..default() - }) + )) ``` - Replace all uses of `ButtonBundle` with `Button`. e.g. ```diff .spawn(( - ButtonBundle { - style: Style { - width: Val::Px(w), - height: Val::Px(h), - // horizontally center child text - justify_content: JustifyContent::Center, - // vertically center child text - align_items: AlignItems::Center, - margin: UiRect::all(Val::Px(20.0)), - ..default() - }, - image: image.clone().into(), + Button, + Style { + width: Val::Px(w), + height: Val::Px(h), + // horizontally center child text + justify_content: JustifyContent::Center, + // vertically center child text + align_items: AlignItems::Center, + margin: UiRect::all(Val::Px(20.0)), ..default() }, + UiImage::from(image.clone()), ImageScaleMode::Sliced(slicer.clone()), )) ``` - Replace all uses of `ImageBundle` with `UiImage`. e.g. ```diff - commands.spawn(ImageBundle { - image: UiImage { + commands.spawn(( + UiImage { texture: metering_mask, ..default() }, - style: Style { + Style { width: Val::Percent(100.0), height: Val::Percent(100.0), ..default() }, - ..default() - }); + )); ``` --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com> |
||
![]() |
7482a0d26d
|
aligning public apis of Time,Timer and Stopwatch (#15962)
Fixes #15834 ## Migration Guide The APIs of `Time`, `Timer` and `Stopwatch` have been cleaned up for consistency with each other and the standard library's `Duration` type. The following methods have been renamed: - `Stowatch::paused` -> `Stopwatch::is_paused` - `Time::elapsed_seconds` -> `Time::elasped_secs` (including `_f64` and `_wrapped` variants) |
||
![]() |
f602edad09
|
Text Rework cleanup (#15887)
# Objective Cleanup naming and docs, add missing migration guide after #15591 All text root nodes now use `Text` (UI) / `Text2d`. All text readers/writers use `Text<Type>Reader`/`Text<Type>Writer` convention. --- ## Migration Guide Doubles as #15591 migration guide. Text bundles (`TextBundle` and `Text2dBundle`) were removed in favor of `Text` and `Text2d`. Shared configuration fields were replaced with `TextLayout`, `TextFont` and `TextColor` components. Just `TextBundle`'s additional field turned into `TextNodeFlags` component, while `Text2dBundle`'s additional fields turned into `TextBounds` and `Anchor` components. Text sections were removed in favor of hierarchy-based approach. For root text entities with `Text` or `Text2d` components, child entities with `TextSpan` will act as additional text sections. To still access text spans by index, use the new `TextUiReader`, `Text2dReader` and `TextUiWriter`, `Text2dWriter` system parameters. |
||
![]() |
bdd0af6bfb
|
Deprecate SpatialBundle (#15830)
# Objective - Required components replace bundles, but `SpatialBundle` is yet to be deprecated ## Solution - Deprecate `SpatialBundle` - Insert `Transform` and `Visibility` instead in examples using it - In `spawn` or `insert` inserting a default `Transform` or `Visibility` with component already requiring either, remove those components from the tuple ## Testing - Did you test these changes? If so, how? Yes, I ran the examples I changed and tests - Are there any parts that need more testing? The `gamepad_viewer` and and `custom_shader_instancing` examples don't work as intended due to entirely unrelated code, didn't check main. - How can other people (reviewers) test your changes? Is there anything specific they need to know? Run examples, or just check that all spawned values are identical - If relevant, what platforms did you test these changes on, and are there any important ones you can't test? Linux, wayland trough x11 (cause that's the default feature) --- ## Migration Guide `SpatialBundle` is now deprecated, insert `Transform` and `Visibility` instead which will automatically insert all other components that were in the bundle. If you do not specify these values and any other components in your `spawn`/`insert` call already requires either of these components you can leave that one out. before: ```rust commands.spawn(SpatialBundle::default()); ``` after: ```rust commands.spawn((Transform::default(), Visibility::default()); ``` |
||
![]() |
6f7d0e5725
|
split up TextStyle (#15857)
# Objective Currently text is recomputed unnecessarily on any changes to its color, which is extremely expensive. ## Solution Split up `TextStyle` into two separate components `TextFont` and `TextColor`. ## Testing I added this system to `many_buttons`: ```rust fn set_text_colors_changed(mut colors: Query<&mut TextColor>) { for mut text_color in colors.iter_mut() { text_color.set_changed(); } } ``` reports ~4fps on main, ~50fps with this PR. ## Migration Guide `TextStyle` has been renamed to `TextFont` and its `color` field has been moved to a separate component named `TextColor` which newtypes `Color`. |
||
![]() |
6ad6eaa873
|
Fix println in morph_targets example (#15851)
# Objective This example uses `println` from a system, which we don't advise people do. It also gives no context for the debug prints, which I assumed to be stray debug code at first. ## Solution Use `info!`, and add a small amount of context so the console output looks deliberate. ## Testing `cargo run --example morph_targets` |
||
![]() |
3da0ef048e
|
Remove the Component trait implementation from Handle (#15796)
# Objective - Closes #15716 - Closes #15718 ## Solution - Replace `Handle<MeshletMesh>` with a new `MeshletMesh3d` component - As expected there were some random things that needed fixing: - A couple tests were storing handles just to prevent them from being dropped I believe, which seems to have been unnecessary in some. - The `SpriteBundle` still had a `Handle<Image>` field. I've removed this. - Tests in `bevy_sprite` incorrectly added a `Handle<Image>` field outside of the `Sprite` component. - A few examples were still inserting `Handle`s, switched those to their corresponding wrappers. - 2 examples that were still querying for `Handle<Image>` were changed to query `Sprite` ## Testing - I've verified that the changed example work now ## Migration Guide `Handle` can no longer be used as a `Component`. All existing Bevy types using this pattern have been wrapped in their own semantically meaningful type. You should do the same for any custom `Handle` components your project needs. The `Handle<MeshletMesh>` component is now `MeshletMesh3d`. The `WithMeshletMesh` type alias has been removed. Use `With<MeshletMesh3d>` instead. |
||
![]() |
a6be9b4ccd
|
Rename TextBlock to TextLayout (#15797)
# Objective - Improve clarity when spawning a text block. See [this discussion](https://github.com/bevyengine/bevy/pull/15591/#discussion_r1787083571). ## Solution - Rename `TextBlock` to `TextLayout`. |
||
![]() |
c2c19e5ae4
|
Text rework (#15591)
**Ready for review. Examples migration progress: 100%.** # Objective - Implement https://github.com/bevyengine/bevy/discussions/15014 ## Solution This implements [cart's proposal](https://github.com/bevyengine/bevy/discussions/15014#discussioncomment-10574459) faithfully except for one change. I separated `TextSpan` from `TextSpan2d` because `TextSpan` needs to require the `GhostNode` component, which is a `bevy_ui` component only usable by UI. Extra changes: - Added `EntityCommands::commands_mut` that returns a mutable reference. This is a blocker for extension methods that return something other than `self`. Note that `sickle_ui`'s `UiBuilder::commands` returns a mutable reference for this reason. ## Testing - [x] Text examples all work. --- ## Showcase TODO: showcase-worthy ## Migration Guide TODO: very breaking ### Accessing text spans by index Text sections are now text sections on different entities in a hierarchy, Use the new `TextReader` and `TextWriter` system parameters to access spans by index. Before: ```rust fn refresh_text(mut query: Query<&mut Text, With<TimeText>>, time: Res<Time>) { let text = query.single_mut(); text.sections[1].value = format_time(time.elapsed()); } ``` After: ```rust fn refresh_text( query: Query<Entity, With<TimeText>>, mut writer: UiTextWriter, time: Res<Time> ) { let entity = query.single(); *writer.text(entity, 1) = format_time(time.elapsed()); } ``` ### Iterating text spans Text spans are now entities in a hierarchy, so the new `UiTextReader` and `UiTextWriter` system parameters provide ways to iterate that hierarchy. The `UiTextReader::iter` method will give you a normal iterator over spans, and `UiTextWriter::for_each` lets you visit each of the spans. --------- Co-authored-by: ickshonpe <david.curthoys@googlemail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com> |
||
![]() |
7d40e3ec87
|
Migrate bevy_sprite to required components (#15489)
# Objective Continue migration of bevy APIs to required components, following guidance of https://hackmd.io/@bevy/required_components/ ## Solution - Make `Sprite` require `Transform` and `Visibility` and `SyncToRenderWorld` - move image and texture atlas handles into `Sprite` - deprecate `SpriteBundle` - remove engine uses of `SpriteBundle` ## Testing ran cargo tests on bevy_sprite and tested several sprite examples. --- ## Migration Guide Replace all uses of `SpriteBundle` with `Sprite`. There are several new convenience constructors: `Sprite::from_image`, `Sprite::from_atlas_image`, `Sprite::from_color`. WARNING: use of `Handle<Image>` and `TextureAtlas` as components on sprite entities will NO LONGER WORK. Use the fields on `Sprite` instead. I would have removed the `Component` impls from `TextureAtlas` and `Handle<Image>` except it is still used within ui. We should fix this moving forward with the migration. |
||
![]() |
219b5930f1
|
Rename App/World::observe to add_observer , EntityWorldMut::observe_entity to observe . (#15754)
# Objective - Closes #15752 Calling the functions `App::observe` and `World::observe` doesn't make sense because you're not "observing" the `App` or `World`, you're adding an observer that listens for an event that occurs *within* the `World`. We should rename them to better fit this. ## Solution Renames: - `App::observe` -> `App::add_observer` - `World::observe` -> `World::add_observer` - `Commands::observe` -> `Commands::add_observer` - `EntityWorldMut::observe_entity` -> `EntityWorldMut::observe` (Note this isn't a breaking change as the original rename was introduced earlier this cycle.) ## Testing Reusing current tests. |
||
![]() |
700123ec64
|
Replace Handle<AnimationGraph> component with a wrapper (#15742)
# Objective - Closes #15717 ## Solution - Wrap the handle in a new wrapper component: `AnimationGraphHandle`. ## Testing Searched for all instances of `AnimationGraph` in the examples and updated and tested those ## Migration Guide `Handle<AnimationGraph>` is no longer a component. Instead, use the `AnimationGraphHandle` component which contains a `Handle<AnimationGraph>`. |
||
![]() |
26813d9732
|
easing_functions example: draw point closer to its curve (#15744)
# Objective - After #15711 which added a column to the example, the point of a curve was too close to the next curve ## Solution - Make it closer to its own |