mirror of
https://github.com/bevyengine/bevy.git
synced 2026-06-30 15:55:32 -04:00
create-pull-request/patch
43 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c3c118c20c |
Add support for custom picking data (#23245)
# Objective - Adds the ability for picking backends to add custom hit data - Fixes #16186 ## Solution - Adds an `extra` field to the HitData struct which can take any Data that implements HitDataExtra. This is stored in an Arc which can be downcast with a helper function in the system that consumes the hit. - In the original ticket I suggested using a generic, however this caused extensive code changes and complications down the line with the HoverMap and OverMap so I decided against it. ## Testing - I added an example custom_hit_data to test the new feature - I tested all other picking examples to ensure they still work as expected - I tested the examples on Ubuntu - I additionally tested the custom_hit_data example in wasm --- ## Showcase <details> <summary>Click to view showcase</summary> ### Creating custom hits: ```rust let picks: Vec<(Entity, HitData)> = ray_cast .cast_ray(ray, &settings) .iter() .map(|(entity, hit)| { let extra = TriangleHitInfo { triangle_vertices: hit.triangle, }; let hit_data = HitData::new_with_extra( ray_id.camera, hit.distance, Some(hit.point), Some(hit.normal), extra, ); (*entity, hit_data) }) .collect(); ``` ### Reading custom hits: ```rust for hits in pointer_hits.read() { for (_, hit) in &hits.picks { let Some(info) = hit.extra_as::<TriangleHitInfo>() else { continue; }; let Some(vertices) = info.triangle_vertices else { continue; }; // do something cool with your custom hit data } } ``` ### An example of what you can do with this <img width="1291" height="730" alt="image" src="https://github.com/user-attachments/assets/2d0e8aed-7059-470a-a0f6-1453356cfe6a" /> </details> |
||
|
|
d1dc09ae19 |
Use the shorthand functions to construct Vals and UiRects in examples (#22765)
# Objective - Fixes #22753 ## Solution - Replace `Val::Px` and `Val::Auto` with `px()` and `auto()` in examples ## Testing - None |
||
|
|
7bc8892c16 |
Update some UI examples (#22701)
# Objective Some UI examples use the Val and UiRect constructor methods inconsistantly or not at all. ## Solution Update some UI examples to use the val and uirect helper functions. ## Testing The output of the examples should be unchanged |
||
|
|
a88af65738 |
Contact Shadows (#22382)
# Objective - Implement contact shadows to add fine shadow detail where shadow cascades cannot. ## Solution - Extend our existing pbr implementation using our existing raymarching functions. --- ## Showcase <img width="1824" height="1180" alt="image" src="https://github.com/user-attachments/assets/e93b79c5-c596-4a9e-b94d-20bdde1d863b" /> <img width="1824" height="1180" alt="image" src="https://github.com/user-attachments/assets/0fd7dffa-60b8-4b92-8fad-7f993d4d89dd" /> https://github.com/user-attachments/assets/e74b190d-9ae3-4aaf-97f0-b520930a0667 https://github.com/user-attachments/assets/e80ccb26-bbaa-4d25-a823-8ea12354c5b9 https://github.com/user-attachments/assets/b04f4b00-92bd-4a2f-b7dd-5157d8fbe0ab <img width="1073" height="685" alt="image" src="https://github.com/user-attachments/assets/b7629908-dd32-48db-8ee7-a4d2dd8f66c2" /> <img width="1073" height="685" alt="image" src="https://github.com/user-attachments/assets/3de0258e-9191-4180-ac57-41b32e1205bd" /> <img width="1073" height="685" alt="image" src="https://github.com/user-attachments/assets/951477f9-e9a9-426f-ae8d-18ae50cc7b85" /> <img width="1073" height="685" alt="image" src="https://github.com/user-attachments/assets/2291453c-da57-4fcc-a6b0-f60f6eac6cbb" /> <img width="1073" height="685" alt="image" src="https://github.com/user-attachments/assets/5820cdff-ea54-4294-b520-2a8d8dc24996" /> <img width="1073" height="685" alt="image" src="https://github.com/user-attachments/assets/3ea16481-7689-4e99-87e2-1589f1532e4c" /> --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: charlotte 🌸 <charlotte.c.mcelwain@gmail.com> |
||
|
|
30a467d44e |
Add drag and drop picking example (#22214)
# Objective Fixes #21894 ## Solution As suggested in the issue adds the drag drop example from https://github.com/bevyengine/bevy/issues/21849 to the picking examples. ## Testing Change is trivial, adding an example so simply ran, and checked it matched the expected behaviour ## Showcase <img width="2250" height="1045" alt="image" src="https://github.com/user-attachments/assets/199e7bb8-a309-45b6-898e-c4f203841914" /> --- --------- Co-authored-by: ickshonpe <david.curthoys@googlemail.com> |
||
|
|
eda118d033 |
Event Rearchitecture (#20731)
There is general consensus that our terminology for Events, "entity events", Observers, and BufferedEvents needs clarity. Additionally, many of us also agree that the current Observer system would benefit from additional static-ness: currently it is assumed that you can use events in pretty much any context, and they all go through the exact same code path. Alice put forth a proposal to [Overhaul Observers](https://hackmd.io/@bevy/rk4S92hmlg), and we have already partially implemented it for 0.17. I think it does a great job of outlining many of the issues at play, and it solves them reasonably well. But I _also_ think the proposed solution isn't yet ideal. Given that it is already partially implemented for 0.17, it is a breaking change, _and_ given that we have already broken the Observer API a number of times, I think we need to sort this out before the next release. This is a big changeset, but it is _largely_ just a reframing of what is already there. I haven't fundamentally changed the behaviors. I've just refined and constrained in a way that allows us to do what we are currently doing in a clearer, simpler, and more performant way. First, I'll give some quick notes on Alice's proposal (which you all should read if you haven't yet!): ### Notes on Alice's Proposal - I like the move toward a more static API - I think we've gone too far down the "separate terminology" path. The proposal introduces a zoo of apis, terms, and "subterms". I think we need to simplify our concepts and names to make this all easier to talk about and use in practice. - BroadcastEvent feels like the wrong name. EntityEvent is also "broadcast" in the exact same way - BufferedEvent is a completely different system than EntityEvent and BroadcastEvent. This muddles concepts too much. It needs its own standalone, single-word concept name. - "Universal observers": I think this should be fully context driven, rather than needing encoding in the API. - I agree we can't get rid of buffered events, and that merging them with "broadcast events" isn't helpful - I'm not quite sure how we'd make the proposed PropagateEvent subtrait work transparently. This can't be "layered on top" as a trait. It needs to be baked in at more fundamental level. * I don't like `app.add_broadcast_observers()`, `app.add_universal_observers()`, `Observer::entity_observer`, `Observer::broadcast`, etc. The `On` event should statically determine whether an observer is an "entity observer" or a "broadcast" Observer. This would already be encoded in the type system and is therefore something we can do on the developer's behalf. Likewise, any observer being registered at a top level is inherently _not_ a specific entity observer. All of these variants serve to make users guess and poke around in a way that is unnecessary. I want simple one word concept names, single constructors, etc. ### Proposed Principals - Static-ness: - Events should only be usable in the context they were defined to be used. - When triggered, Observers should *only* have access to fields and behaviors that are relevant: - Dont return Option or PLACEHOLDER: the field or function shouldn't exist - Entity events that don't support propagation shouldn't expose that functionality - Don't do unnecessary work at runtime - Event triggers shouldn't branch through every potential event code path - Don't clone potentially large lists of event context unnecessarily (Ex: we currently clone the component list for every observer invocation) - Minimize codegen - Don't recompile things redundantly. - Don't compile unnecessary code paths. - Clear and Simple - Minimize the number of concept names floating around, and lock each concept down heavily to a specific context - I'm convinced at this point that "buffered events" and "observer events" sharing concept names is wrong. We need two clean and clear terms, and I'm willing to give "buffered events" a slightly worse name if it means "observer events" can be nicer. - Don't throw the concept name "Event" out ... it is a very good name. Instead, constrain it to one specific thing. - Minimize our API surface - Events contain all context, including what used to be the "target". This lets people define the "target" name that makes the most sense for the context, and lets the documentation fully describe the context of that "target". ### Concepts - **Event** (the thing you "observe") - Rationale: "Event" is the clear choice for this concept. An "event" feels like something that happens in real time. "Event observers" are things that observe events when they occur (are triggered). Additionally, this is the concept that "propagates", and "event propagation" is a term people understand. - **Trigger**: (the verb that "causes" events to happen for targets). Events are Triggered. This can include additional context/ data that is passed to observers / informs the trigger behavior. Events have _exactly_ one Trigger. If you want a different trigger behavior, define a new event. This makes the system more static, more predictable, and easier to understand and document. `world.trigger_ref_with` makes it possible to pass in mutable reference to your own Trigger data, making it possible to customize the input trigger data and read out the final trigger data. - **Observer** (the thing that "observes" events): An event's `Trigger` determines which observers will run. - **Event Types**: You can build any "type" of event. The concept of a "target" has been removed. Instead, define a `Trigger` that expects a specific kind of event (ex: `E: EntityEvent`). - **EntityEvent** We add a new `EntityEvent` trait, which defines an `event.entity()` accessor. This is used by the `Trigger` impls : `EntityTrigger`, `PropagateEntityTrigger`, and `EntityComponentsTrigger`. - **Message** (the buffered thing you "read" and "write") - `Message` is a solid metaphor for what this is ... it is data that is written and then at some later point read by someone / something else. I expect existing consumers of "buffered events" to lament this name change, as "event" feels nicer. But having a separate name is within everyone's best interest. - **MessageReader** (the thing that reads messages) - **MessageWriter** (the thing that writes messages) ### The Changes - `Event` trait changes - Event is now used exclusively by Observers - Added `Event::Trigger`, which defines what trigger implementation this event will use - Added the `Trigger` trait - All of the shared / hard-coded observer trigger logic has been broken out into individual context-specific Trigger traits. - "Trigger Targets" have been removed. - Instead, Events, in combination with their Trigger impl, decide how they will be triggered. In general, this means that Events now include their "targets" as fields on the event. - APIs like `trigger_targets` have been replaced by `trigger`, which can now be used for any `Event` - `EntityEvent` trait changes - Propagation config has been removed from the `EntityEvent` trait. It now lives on the `Trigger` trait (specifically the `PropagateEntityTrigger` trait). - `EntityEvent` now provides `entity / entity_mut` accessors for the Event it is implemented for - `EntityEvent` defaults to having no propagation (uses the simpler `EntityTrigger`) - `#[entity_event(propagate)]` enables the "default" propagation logic (uses ChildOf). The existing `#[entity_event(traversal = X)]` has been renamed to `#[entity_event(propagate = X)` - Deriving `EntityEvent` requires either a single `MyEvent(Entity)`, the `entity` field name (`MyEvent { entity: Entity}`), or `MyEvent { #[event_entity] custom: Entity }` - Animation event changes - Animation events now have their own `AnimationEvent` trait, which sets the `AnimationEventTrigger`. This allows developers to pass in events that _dont_ include the Entity field (as this is set by the system). The custom trigger also opens the doors to cheaply passing in additional animation system context, accessible through `On` - `EntityComponentsTrigger` - The built in Add/Remove/etc lifecycle events now use the `EntityComponentsTrigger`, which passes in the components as additional state. This _significantly_ cuts down on clones, as it does a borrow rather than cloning the list into _each_ observer execution. - Each event now has an `entity` field. - Style changes - Prefer the event name for variables: `explode: On<Explode>` not `event: On<Explode>` - Prefer using the direct field name for the entity on entity events, rather than `event.entity()`. This allows us to use more specific names where appropriate, provides better / more contextual docs, and coaches developers to think of `On<MyEvent>` _as_ the event itself. Take a look at the changes to the examples and the built-in events to see what this looks like in practice. ### Downsides - Moving the "target" into the event adds some new constraints: - Triggering the same event for multiple entities requires multiple trigger calls. For "expensive" events (ex: lots of data attached to the event), this will be more awkward. Your options become: - Create multiple instances of the event, cloning the expensive data - Use `trigger_ref`, and mutate the event on each call to change the target. - Move the "expensive" shared data into the Trigger, and use `trigger_ref_with`` - We could build a new EntityEvent method that abstracts over the "event mutation" behavior and provides something like the old `trigger_target` behavior. - Use a different `EntityTargetTrigger` (not currently provided by bevy, but we could), which brings back the old behavior. This would be used with `trigger_with` to replicate the old pattern: `world.trigger_with(MyEvent, [e1, e2].into())` (or we could make the `into()` implicit) - Bubbling the event involves mutating the event to set the entity. This means that `trigger_ref` will result in the event's `EntityEvent::entity()` being the final bubbled entity instead of the initial entity. - Some APIs (trivially) benefit from the "target entity" being separate from the event. Specifically, this new API requires changes to the "Animation Event" system in AnimationPlayer. I think this is actually a good change set, as it allows us to: - Cheaply expose more animation state as part of a new AnimationEventTrigger impl - Move that "implict" entity target provided by the AnimationPlayer into the AnimationEventTrigger - Encode the "animation event trigger-ness" of the event into the type itself (by requiring `#[event(trigger = AnimationEventTrigger)]`) - By not implementing Default for AnimationEventTrigger, we can block animation events from being fired manually by the user. ### Draft TODO - [x] Fill in documentation and update existing docs - [ ] Benchmark: I expect this impl to be significantly faster. There might also be tangible binary size improvements, as I've removed a lot of redundant codegen. - [x] Update release notes and migration guides ### Next Steps - The `BufferedEvent -> Message` rename was not included to keep the size down. Fixes #19648 --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Jan Hohenheim <jan@hohenheim.ch> |
||
|
|
13877fa84d |
Add a new trait to accept more types in the Val-helper functions (#20551)
# Objective - Allow the `Val`-helper functions to accept more types besides just `f32` Fixes #20549 ## Solution - Adds a new trait that can be implemented for numbers - That trait has a method that converts `self` to `f32` ## Testing - I tested it using Rust's testing framework (although I didn't leave the tests in, as I don't deem them important enough) <details> <summary>Rust test</summary> ```rust #[cfg(test)] mod tests { use super::*; #[test] fn test_val_helpers_work() { let p = px(10_u8); assert_eq!(p, Val::Px(10.0)); let p = px(10_u16); assert_eq!(p, Val::Px(10.0)); let p = px(10_u32); assert_eq!(p, Val::Px(10.0)); let p = px(10_u64); assert_eq!(p, Val::Px(10.0)); let p = px(10_u128); assert_eq!(p, Val::Px(10.0)); let p = px(10_i8); assert_eq!(p, Val::Px(10.0)); let p = px(10_i16); assert_eq!(p, Val::Px(10.0)); let p = px(10_i32); assert_eq!(p, Val::Px(10.0)); let p = px(10_i64); assert_eq!(p, Val::Px(10.0)); let p = px(10_i128); assert_eq!(p, Val::Px(10.0)); let p = px(10.3_f32); assert_eq!(p, Val::Px(10.3)); let p = px(10.6_f64); assert_eq!(p, Val::Px(10.6)); } } ``` </details> --- ## Showcase ```rust // Same as Val::Px(10.) px(10); px(10_u8); px(10.0); ``` |
||
|
|
5058f8a9e6 |
Improve On Terminology (#20648)
# Objective Fixes #19263 (and expands on it) Within `Observers`, we have started to distance ourselves from the "trigger" terminology. Specifically, we have renamed `Trigger` to `On`. I think this was a very good move, but we're currently in an awkward middle ground state. Users interact with `On` as if it were an event: `On<E>` exposes the event and derefs directly to it. I think we should embrace this mindset fully, in the interest of clarity. ## Solution - Rename all `trigger: On<SomeEvent>` cases to `event: On<SomeEvent>`. - Rename `On::target` to `On::entity`. This reads _much_ better when writing `query.get(event.entity())` and pairs more effectively with the `EntityEvent` terminology. - Rename `On::original_target` to `On::original_entity`, for the same reasons. - Take advantage of the `Deref` behavior where appropriate ```rust // Before entity.observe(|trigger: On<Explode>| { println!("{} exploded!", trigger.target()); }) // After entity.observe(|event: On<Explode>| { println!("{} exploded!", event.entity()); }) ``` |
||
|
|
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. |
||
|
|
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.
|
||
|
|
33c6f45a35 |
Rename some pointer events and components (#19574)
# Objective #19366 implemented core button widgets, which included the `Depressed` state component. `Depressed` was chosen instead of `Pressed` to avoid conflict with the `Pointer<Pressed>` event, but it is problematic and awkward in many ways: - Using the word "depressed" for such a high-traffic type is not great due to the obvious connection to "depressed" as in depression. - "Depressed" is not what I would search for if I was looking for a component like this, and I'm not aware of any other engine or UI framework using the term. - `Depressed` is not a very natural pair to the `Pointer<Pressed>` event. - It might be because I'm not a native English speaker, but I have very rarely heard someone say "a button is depressed". Seeing it, my mind initially goes from "depression??" to "oh, de-pressed, meaning released" and definitely not "is pressed", even though that *is* also a valid meaning for it. A related problem is that the current `Pointer<Pressed>` and `Pointer<Released>` event names use a different verb tense than all of our other observer events such as `Pointer<Click>` or `Pointer<DragStart>`. By fixing this and renaming `Pressed` (and `Released`), we can then use `Pressed` instead of `Depressed` for the state component. Additionally, the `IsHovered` and `IsDirectlyHovered` components added in #19366 use an inconsistent naming; the other similar components don't use an `Is` prefix. It also makes query filters like `Has<IsHovered>` and `With<IsHovered>` a bit more awkward. This is partially related to Cart's [picking concept proposal](https://gist.github.com/cart/756e48a149db2838028be600defbd24a?permalink_comment_id=5598154). ## Solution - Rename `Pointer<Pressed>` to `Pointer<Press>` - Rename `Pointer<Released>` to `Pointer<Release>` - Rename `Depressed` to `Pressed` - Rename `IsHovered` to `Hovered` - Rename `IsDirectlyHovered` to `DirectlyHovered` |
||
|
|
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. |
||
|
|
2b59ab8e2d |
Fix Anchor component inconsistancies (#18393)
## Objective Fix the misleading 2d anchor API where `Anchor` is a component and required by `Text2d` but is stored on a field for sprites. Fixes #18367 ## Solution Remove the `anchor` field from `Sprite` and require `Anchor` instead. ## Migration Guide The `anchor` field has been removed from `Sprite`. Instead the `Anchor` component is now a required component on `Sprite`. |
||
|
|
0f6d532a15 |
Sprite picking docs fix (#19016)
# Objective - Docs in sprite picking plugin / example contain outdated information. References: - Sprite picking now always require `Picking` - #17842 - Transparency pass-through added - #16388 ## Solution - Fix the docs. |
||
|
|
84b09b9398 |
Newtype Anchor (#18439)
# Objective The `Anchor` component doesn't need to be a enum. The variants are just mapped to `Vec2`s so it could be changed to a newtype with associated const values, saving the space needed for the discriminator by the enum. Also there was no benefit I think in hiding the underlying `Vec2` representation of `Anchor`s. Suggested by @atlv24. Fixes #18459 Fixes #18460 ## Solution Change `Anchor` to a struct newtyping a `Vec2`, and its variants into associated constants. ## Migration Guide The anchor component has been changed from an enum to a struct newtyping a `Vec2`. The `Custom` variant has been removed, instead to construct a custom `Anchor` use its tuple constructor: ```rust Sprite { anchor: Anchor(Vec2::new(0.25, 0.4)), ..default() } ``` The other enum variants have been replaced with corresponding constants: * `Anchor::BottomLeft` to `Anchor::BOTTOM_LEFT` * `Anchor::Center` to `Anchor::CENTER` * `Anchor::TopRight` to `Anchor::TOP_RIGHT` * .. and so on for the remaining variants |
||
|
|
5ab0456f61 |
Unified picking cleanup (#18401)
# Objective @cart noticed some issues with my work in https://github.com/bevyengine/bevy/pull/17348#discussion_r2001815637, which I somehow missed before merging the PR. ## Solution - feature gate the UiPickingPlugin correctly - don't manually add the picking plugins ## Testing Ran the debug_picking and sprite_picking examples (for UI and sprites respectively): both seem to work fine. |
||
|
|
65e289f5bc |
Unify picking backends (#17348)
# Objective Currently, our picking backends are inconsistent: - Mesh picking and sprite picking both have configurable opt in/out behavior. UI picking does not. - Sprite picking uses `SpritePickingCamera` and `Pickable` for control, but mesh picking uses `RayCastPickable`. - `MeshPickingPlugin` is not a part of `DefaultPlugins`. `SpritePickingPlugin` and `UiPickingPlugin` are. ## Solution - Add configurable opt in/out behavior to UI picking (defaults to opt out). - Replace `RayCastPickable` with `MeshPickingCamera` and `Pickable`. - Remove `SpritePickingPlugin` and `UiPickingPlugin` from `DefaultPlugins`. ## Testing Ran some examples. ## Migration Guide `UiPickingPlugin` and `SpritePickingPlugin` are no longer included in `DefaultPlugins`. They must be explicitly added. `RayCastPickable` has been replaced in favor of the `MeshPickingCamera` and `Pickable` components. You should add them to cameras and entities, respectively, if you have `MeshPickingSettings::require_markers` set to `true`. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
|
|
f7b2a02224 |
Make sprite picking opt-in (#17842)
# Objective Fix https://github.com/bevyengine/bevy/issues/17108 See https://github.com/bevyengine/bevy/issues/17108#issuecomment-2653020889 ## Solution - Make the query match `&Pickable` instead `Option<&Pickable>` ## Testing - Run the `sprite_picking` example and everything still work ## Migration Guide - Sprite picking are now opt-in, make sure you insert `Pickable` component when using sprite picking. ```diff -commands.spawn(Sprite { .. } ); +commands.spawn((Sprite { .. }, Pickable::default()); ``` |
||
|
|
02bb151889 |
Rename PickingBehavior to Pickable (#17266)
# Objective PR #17225 allowed for sprite picking to be opt-in. After some discussion, it was agreed that `PickingBehavior` should be used to opt-in to sprite picking behavior for entities. This leads to `PickingBehavior` having two purposes: mark an entity for use in a backend, and describe how it should be picked. Discussion led to the name `Pickable`making more sense (also: this is what the component was named before upstreaming). A follow-up pass will be made after this PR to unify backends. ## Solution Replace all instances of `PickingBehavior` and `picking_behavior` with `Pickable` and `pickable`, respectively. ## Testing CI ## Migration Guide Change all instances of `PickingBehavior` to `Pickable`. |
||
|
|
0a9740c18f |
Make sprite picking opt-in (#17225)
# Objective Fixes #16903. ## Solution - Make sprite picking opt-in by requiring a new `SpritePickingCamera` component for cameras and usage of a new `Pickable` component for entities. - Update the `sprite_picking` example to reflect these changes. - Some reflection cleanup (I hope that's ok). ## Testing Ran the `sprite_picking` example ## Open Questions <del> <ul> <li>Is the name `SpritePickable` appropriate?</li> <li>Should `SpritePickable` be in `bevy_sprite::prelude?</li> </ul> </del> ## Migration Guide The sprite picking backend is now strictly opt-in using the `SpritePickingCamera` and `Pickable` components. You should add the `Pickable` component any entities that you want sprite picking to be enabled for, and mark their respective cameras with `SpritePickingCamera`. |
||
|
|
5faff84c10 |
Upstream DebugPickingPlugin from bevy_mod_picking (#17177)
# Objective The debug features (`DebugPickingPlugin`) from `bevy_mod_picking` were not upstreamed with the rest of the core changes, this PR reintroduces it for usage inside `bevy_dev_tools` ## Solution Vast majority of this code is taken as-is from `bevy_mod_picking` aside from changes to ensure compilation and code style, as such @aevyrie was added as the co-author for this change. ### Main changes * `multiselection` support - the relevant code was explicitly not included in the process of upstreaming the rest of the package, so it also has been omitted here. * `bevy_egui` support - the old package had a preference for using `bevy_egui` instead of `bevy_ui` if possible, I couldn't see a way to support this in a core crate, so this has been removed. Relevant code has been added to the `bevy_dev_tools` crate instead of `bevy_picking` as it is a better fit and requires a dependency on `bevy_ui` for drawing debug elements. ### Minor changes * Changed the debug text size from `60` to `12` as the former was so large as to be unreadable in the new example. ## Testing * `cargo run -p ci` * Added a new example in `dev_tools/picking_debug` and visually verified the in-window results and the console messages --------- Co-authored-by: Aevyrie <aevyrie@gmail.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
|
|
ad4144ad7a |
Rename Pointer<Down/Up> -> Pointer<Pressed/Released> in bevy_picking. (#16331)
# Objective Fixes #16192 ## Solution I renamed the Pointer<Down/Up> to <Pressed/Released> and then I resolved all the errors. Renamed variables like "is_down" to "is_pressed" to maintain consistency. Modified the docs in places where 'down/up' were used to maintain consistency. ## Testing I haven't tested this in any way beside the checks from rust analyzer and the examples in the examples/ directory. --- ## Migration Guide ### `bevy_picking/src/pointer.rs`: #### `enum PressDirection`: - `PressDirection::Down` changes to `PressDirection::Pressed`. - `PressDirection::Up` changes to `PressDirection::Released`. These changes are also relevant when working with `enum PointerAction` ### `bevy_picking/src/events.rs`: Clicking and pressing Events in events.rs categories change from [Down], [Up], [Click] to [Pressed], [Released], [Click]. - `struct Down` changes to `struct Pressed` - fires when a pointer button is pressed over the 'target' entity. - `struct Up` changes to `struct Released` - fires when a pointer button is released over the 'target' entity. - `struct Click` now fires when a pointer sends a Pressed event followed by a Released event on the same 'target'. - `struct DragStart` now fires when the 'target' entity receives a pointer Pressed event followed by a pointer Move event. - `struct DragEnd` now fires when the 'target' entity is being dragged and receives a pointer Released event. - `PickingEventWriters<'w>::down_events: EventWriter<'w, Pointer<Down>>` changes to `PickingEventWriters<'w>::pressed_events: EventWriter<'w, Pointer<Pressed>>`. - `PickingEventWriters<'w>::up_events changes to PickingEventWriters<'w>::released_events`. --------- Co-authored-by: Harun Ibram <harun.ibram@outlook.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
|
|
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` |
||
|
|
03372e590d |
Picking example touchups (#16129)
# Objective - Cleanup pass to make the examples a bit more succinct, focusing on the topic at hand. - Added drag rotation to make the picking examples more interesting, and to demonstrate a simple use case. |
||
|
|
54b323ec80 |
Mesh picking fixes (#16110)
# Objective - Mesh picking is noisy when a non triangle list is used - Mesh picking runs even when users don't need it - Resolve #16065 ## Solution - Don't add the mesh picking plugin by default - Remove error spam |
||
|
|
7c593179e3 |
Fix bevy_picking plugin suffixes (#16082)
# Objective - `MeshPickingBackend` and `SpritePickingBackend` do not have the `Plugin` suffix - `DefaultPickingPlugins` is masquerading as a `Plugin` when in reality it should be a `PluginGroup` - Fixes #16081. ## Solution - Rename some structures: |Original Name|New Name| |-|-| |`MeshPickingBackend`|`MeshPickingPlugin`| |`MeshPickingBackendSettings`|`MeshPickingSettings`| |`SpritePickingBackend`|`SpritePickingPlugin`| |`UiPickingBackendPlugin`|`UiPickingPlugin`| - Make `DefaultPickingPlugins` a `PluginGroup`. - Because `DefaultPickingPlugins` is within the `DefaultPlugins` plugin group, I also added support for nested plugin groups to the `plugin_group!` macro. ## Testing - I used ripgrep to ensure all references were properly renamed. - For the `plugin_group!` macro, I used `cargo expand` to manually inspect the expansion of `DefaultPlugins`. --- ## Migration Guide > [!NOTE] > > All 3 of the changed structures were added after 0.14, so this does not need to be included in the 0.14 to 0.15 migration guide. - `MeshPickingBackend` is now named `MeshPickingPlugin`. - `MeshPickingBackendSettings` is now named `MeshPickingSettings`. - `SpritePickingBackend` is now named `SpritePickingPlugin`. - `UiPickingBackendPlugin` is now named `UiPickingPlugin`. - `DefaultPickingPlugins` is now a a `PluginGroup` instead of a `Plugin`. |
||
|
|
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(); } } ``` |
||
|
|
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) |
||
|
|
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()); ``` |
||
|
|
0e30b68b20 |
Add mesh picking backend and MeshRayCast system parameter (#15800)
# Objective Closes #15545. `bevy_picking` supports UI and sprite picking, but not mesh picking. Being able to pick meshes would be extremely useful for various games, tools, and our own examples, as well as scene editors and inspectors. So, we need a mesh picking backend! Luckily, [`bevy_mod_picking`](https://github.com/aevyrie/bevy_mod_picking) (which `bevy_picking` is based on) by @aevyrie already has a [backend for it](https://github.com/aevyrie/bevy_mod_picking/blob/74f0c3c0fbc8048632ba46fd8f14e26aaea9c76c/backends/bevy_picking_raycast/src/lib.rs) using [`bevy_mod_raycast`](https://github.com/aevyrie/bevy_mod_raycast). As a side product of adding mesh picking, we also get support for performing ray casts on meshes! ## Solution Upstream a large chunk of the immediate-mode ray casting functionality from `bevy_mod_raycast`, and add a mesh picking backend based on `bevy_mod_picking`. Huge thanks to @aevyrie who did all the hard work on these incredible crates! All meshes are pickable by default. Picking can be disabled for individual entities by adding `PickingBehavior::IGNORE`, like normal. Or, if you want mesh picking to be entirely opt-in, you can set `MeshPickingBackendSettings::require_markers` to `true` and add a `RayCastPickable` component to the desired camera and target entities. You can also use the new `MeshRayCast` system parameter to cast rays into the world manually: ```rust fn ray_cast_system(mut ray_cast: MeshRayCast, foo_query: Query<(), With<Foo>>) { let ray = Ray3d::new(Vec3::ZERO, Dir3::X); // Only ray cast against entities with the `Foo` component. let filter = |entity| foo_query.contains(entity); // Never early-exit. Note that you can change behavior per-entity. let early_exit_test = |_entity| false; // Ignore the visibility of entities. This allows ray casting hidden entities. let visibility = RayCastVisibility::Any; let settings = RayCastSettings::default() .with_filter(&filter) .with_early_exit_test(&early_exit_test) .with_visibility(visibility); // Cast the ray with the settings, returning a list of intersections. let hits = ray_cast.cast_ray(ray, &settings); } ``` This is largely a direct port, but I did make several changes to match our APIs better, remove things we don't need or that I think are unnecessary, and do some general improvements to code quality and documentation. ### Changes Relative to `bevy_mod_raycast` and `bevy_mod_picking` - Every `Raycast` and "raycast" has been renamed to `RayCast` and "ray cast" (similar reasoning as the "Naming" section in #15724) - `Raycast` system param has been renamed to `MeshRayCast` to avoid naming conflicts and to be explicit that it is not for colliders - `RaycastBackend` has been renamed to `MeshPickingBackend` - `RayCastVisibility` variants are now `Any`, `Visible`, and `VisibleInView` instead of `Ignore`, `MustBeVisible`, and `MustBeVisibleAndInView` - `NoBackfaceCulling` has been renamed to `RayCastBackfaces`, to avoid implying that it affects the rendering of backfaces for meshes (it doesn't) - `SimplifiedMesh` and `RayCastBackfaces` live near other ray casting API types, not in their own 10 LoC module - All intersection logic and types are in the same `intersections` module, not split across several modules - Some intersection types have been renamed to be clearer and more consistent - `IntersectionData` -> `RayMeshHit` - `RayHit` -> `RayTriangleHit` - General documentation and code quality improvements ### Removed / Not Ported - Removed unused ray helpers and types, like `PrimitiveIntersection` - Removed getters on intersection types, and made their properties public - There is no `2d` feature, and `Raycast::mesh_query` and `Raycast::mesh2d_query` have been merged into `MeshRayCast::mesh_query`, which handles both 2D and 3D - I assume this existed previously because `Mesh2dHandle` used to be in `bevy_sprite`. Now both the 2D and 3D mesh are in `bevy_render`. - There is no `debug` feature or ray debug rendering - There is no deferred API (`RaycastSource`) - There is no `CursorRayPlugin` (the picking backend handles this) ### Note for Reviewers In case it's helpful, the [first commit](https://github.com/bevyengine/bevy/commit/281638ef10ebd25f783bee2533509fdf756f7ab4) here is essentially a one-to-one port. The rest of the commits are primarily refactoring and cleaning things up in the ways listed earlier, as well as changes to the module structure. It may also be useful to compare the original [picking backend](https://github.com/aevyrie/bevy_mod_picking/blob/74f0c3c0fbc8048632ba46fd8f14e26aaea9c76c/backends/bevy_picking_raycast/src/lib.rs) and [`bevy_mod_raycast`](https://github.com/aevyrie/bevy_mod_raycast) to this PR. Feel free to mention if there are any changes that I should revert or something I should not include in this PR. ## Testing I tested mesh picking and relevant components in some examples, for both 2D and 3D meshes, and added a new `mesh_picking` example. I also ~~stole~~ ported over the [ray-mesh intersection benchmark](https://github.com/aevyrie/bevy_mod_raycast/blob/dbc5ef32fe48997a1a7eeec7434d9dd8b829e52e/benches/ray_mesh_intersection.rs) from `bevy_mod_raycast`. --- ## Showcase Below is a version of the `2d_shapes` example modified to demonstrate 2D mesh picking. This is not included in this PR. https://github.com/user-attachments/assets/7742528c-8630-4c00-bacd-81576ac432bf And below is the new `mesh_picking` example: https://github.com/user-attachments/assets/b65c7a5a-fa3a-4c2d-8bbd-e7a2c772986e There is also a really cool new `mesh_ray_cast` example ported over from `bevy_mod_raycast`: https://github.com/user-attachments/assets/3c5eb6c0-bd94-4fb0-bec6-8a85668a06c9 --------- Co-authored-by: Aevyrie <aevyrie@gmail.com> Co-authored-by: Trent <2771466+tbillington@users.noreply.github.com> Co-authored-by: François Mockers <mockersf@gmail.com> |
||
|
|
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`.
|
||
|
|
bd0c74644f |
Fix sprite and picking examples (#15803)
# Objective Looks like #15489 broke some examples :) And there are some other issues as well. Gabe's brother Gabe is tiny in the `sprite_animation` example:  Gabe is not running in the `sprite_picking` example, and (unrelated) is also very blurry: (note that the screenshot is a bit zoomed in)  Unrelated to sprites, the text in the `simple_picking` example is way too dark when hovered:  ## Solution Both Gabes are now the correct size:  Gabe is crisp and running:  The text has better contrast:  |
||
|
|
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. |
||
|
|
25bfa80e60 |
Migrate cameras to required components (#15641)
# Objective Yet another PR for migrating stuff to required components. This time, cameras! ## Solution As per the [selected proposal](https://hackmd.io/tsYID4CGRiWxzsgawzxG_g#Combined-Proposal-1-Selected), deprecate `Camera2dBundle` and `Camera3dBundle` in favor of `Camera2d` and `Camera3d`. Adding a `Camera` without `Camera2d` or `Camera3d` now logs a warning, as suggested by Cart [on Discord](https://discord.com/channels/691052431525675048/1264881140007702558/1291506402832945273). I would personally like cameras to work a bit differently and be split into a few more components, to avoid some footguns and confusing semantics, but that is more controversial, and shouldn't block this core migration. ## Testing I ran a few 2D and 3D examples, and tried cameras with and without render graphs. --- ## Migration Guide `Camera2dBundle` and `Camera3dBundle` have been deprecated in favor of `Camera2d` and `Camera3d`. Inserting them will now also insert the other components required by them automatically. |
||
|
|
2526410096 |
Clean up the simple_picking example (#15633)
## Solution - Removed superfluous `Pickable` components - Slightly simplified the code for updating the text color - Removed the `Pointer<Click>` observer from the mesh entirely since that doesn't support picking yet |
||
|
|
54006b107b |
Migrate meshes and materials to required components (#15524)
# Objective A big step in the migration to required components: meshes and materials! ## Solution As per the [selected proposal](https://hackmd.io/@bevy/required_components/%2Fj9-PnF-2QKK0on1KQ29UWQ): - Deprecate `MaterialMesh2dBundle`, `MaterialMeshBundle`, and `PbrBundle`. - Add `Mesh2d` and `Mesh3d` components, which wrap a `Handle<Mesh>`. - Add `MeshMaterial2d<M: Material2d>` and `MeshMaterial3d<M: Material>`, which wrap a `Handle<M>`. - Meshes *without* a mesh material should be rendered with a default material. The existence of a material is determined by `HasMaterial2d`/`HasMaterial3d`, which is required by `MeshMaterial2d`/`MeshMaterial3d`. This gets around problems with the generics. Previously: ```rust commands.spawn(MaterialMesh2dBundle { mesh: meshes.add(Circle::new(100.0)).into(), material: materials.add(Color::srgb(7.5, 0.0, 7.5)), transform: Transform::from_translation(Vec3::new(-200., 0., 0.)), ..default() }); ``` Now: ```rust commands.spawn(( Mesh2d(meshes.add(Circle::new(100.0))), MeshMaterial2d(materials.add(Color::srgb(7.5, 0.0, 7.5))), Transform::from_translation(Vec3::new(-200., 0., 0.)), )); ``` If the mesh material is missing, previously nothing was rendered. Now, it renders a white default `ColorMaterial` in 2D and a `StandardMaterial` in 3D (this can be overridden). Below, only every other entity has a material:   Why white? This is still open for discussion, but I think white makes sense for a *default* material, while *invalid* asset handles pointing to nothing should have something like a pink material to indicate that something is broken (I don't handle that in this PR yet). This is kind of a mix of Godot and Unity: Godot just renders a white material for non-existent materials, while Unity renders nothing when no materials exist, but renders pink for invalid materials. I can also change the default material to pink if that is preferable though. ## Testing I ran some 2D and 3D examples to test if anything changed visually. I have not tested all examples or features yet however. If anyone wants to test more extensively, it would be appreciated! ## Implementation Notes - The relationship between `bevy_render` and `bevy_pbr` is weird here. `bevy_render` needs `Mesh3d` for its own systems, but `bevy_pbr` has all of the material logic, and `bevy_render` doesn't depend on it. I feel like the two crates should be refactored in some way, but I think that's out of scope for this PR. - I didn't migrate meshlets to required components yet. That can probably be done in a follow-up, as this is already a huge PR. - It is becoming increasingly clear to me that we really, *really* want to disallow raw asset handles as components. They caused me a *ton* of headache here already, and it took me a long time to find every place that queried for them or inserted them directly on entities, since there were no compiler errors for it. If we don't remove the `Component` derive, I expect raw asset handles to be a *huge* footgun for users as we transition to wrapper components, especially as handles as components have been the norm so far. I personally consider this to be a blocker for 0.15: we need to migrate to wrapper components for asset handles everywhere, and remove the `Component` derive. Also see https://github.com/bevyengine/bevy/issues/14124. --- ## Migration Guide Asset handles for meshes and mesh materials must now be wrapped in the `Mesh2d` and `MeshMaterial2d` or `Mesh3d` and `MeshMaterial3d` components for 2D and 3D respectively. Raw handles as components no longer render meshes. Additionally, `MaterialMesh2dBundle`, `MaterialMeshBundle`, and `PbrBundle` have been deprecated. Instead, use the mesh and material components directly. Previously: ```rust commands.spawn(MaterialMesh2dBundle { mesh: meshes.add(Circle::new(100.0)).into(), material: materials.add(Color::srgb(7.5, 0.0, 7.5)), transform: Transform::from_translation(Vec3::new(-200., 0., 0.)), ..default() }); ``` Now: ```rust commands.spawn(( Mesh2d(meshes.add(Circle::new(100.0))), MeshMaterial2d(materials.add(Color::srgb(7.5, 0.0, 7.5))), Transform::from_translation(Vec3::new(-200., 0., 0.)), )); ``` If the mesh material is missing, a white default material is now used. Previously, nothing was rendered if the material was missing. The `WithMesh2d` and `WithMesh3d` query filter type aliases have also been removed. Simply use `With<Mesh2d>` or `With<Mesh3d>`. --------- Co-authored-by: Tim Blackbird <justthecooldude@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com> |
||
|
|
de888a373d |
Migrate lights to required components (#15554)
# Objective Another step in the migration to required components: lights! Note that this does not include `EnvironmentMapLight` or reflection probes yet, because their API hasn't been fully chosen yet. ## Solution As per the [selected proposals](https://hackmd.io/@bevy/required_components/%2FLLnzwz9XTxiD7i2jiUXkJg): - Deprecate `PointLightBundle` in favor of the `PointLight` component - Deprecate `SpotLightBundle` in favor of the `PointLight` component - Deprecate `DirectionalLightBundle` in favor of the `DirectionalLight` component ## Testing I ran some examples with lights. --- ## Migration Guide `PointLightBundle`, `SpotLightBundle`, and `DirectionalLightBundle` have been deprecated. Use the `PointLight`, `SpotLight`, and `DirectionalLight` components instead. Adding them will now insert the other components required by them automatically. |
||
|
|
1bb8007dce |
Fix typo in sprite_picking.rs example (#15292)
Simple typo I noticed while reading example. |
||
|
|
29508f065f |
Fix floating point math (#15239)
# Objective - Fixes #15236 ## Solution - Use bevy_math::ops instead of std floating point operations. ## Testing - Did you test these changes? If so, how? Unit tests and `cargo run -p ci -- test` - How can other people (reviewers) test your changes? Is there anything specific they need to know? Execute `cargo run -p ci -- test` on Windows. - If relevant, what platforms did you test these changes on, and are there any important ones you can't test? Windows ## Migration Guide - Not a breaking change - Projects should use bevy math where applicable --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: IQuick 143 <IQuick143cz@gmail.com> Co-authored-by: Joona Aalto <jondolf.dev@gmail.com> |
||
|
|
86d5944b2e |
Fix some examples having different instruction text positions (#15017)
# Objective Thought I had found all of these... noticed some `10px` in #15013 and did another sweep. Continuation of #8478, #13583. ## Solution - Position example text (and other elements) 12px from the edge of the screen |
||
|
|
3540b87e17 |
Add bevy_picking sprite backend (#14757)
# Objective Add `bevy_picking` sprite backend as part of the `bevy_mod_picking` upstreamening (#12365). ## Solution More or less a copy/paste from `bevy_mod_picking`, with the changes [here](https://github.com/aevyrie/bevy_mod_picking/pull/354). I'm putting that link here since those changes haven't yet made it through review, so should probably be reviewed on their own. ## Testing I couldn't find any sprite-backend-specific tests in `bevy_mod_picking` and unfortunately I'm not familiar enough with Bevy's testing patterns to write tests for code that relies on windowing and input. I'm willing to break the pointer hit system into testable blocks and add some more modular tests if that's deemed important enough to block, otherwise I can open an issue for adding tests as follow-up. ## Follow-up work - More docs/tests - Ignore pick events on transparent sprite pixels with potential opt-out --------- Co-authored-by: Aevyrie <aevyrie@gmail.com> |
||
|
|
6adf31babf |
hooking up observers and clicking for ui node (#14695)
Makes the newly merged picking usable for UI elements. currently it both triggers the events, as well as sends them as throught commands.trigger_targets. We should probably figure out if this is needed for them all. # Objective Hooks up obserers and picking for a very simple example ## Solution upstreamed the UI picking backend from bevy_mod_picking ## Testing tested with the new example picking/simple_picking.rs --- --------- Co-authored-by: Lixou <82600264+DasLixou@users.noreply.github.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Kristoffer Søholm <k.soeholm@gmail.com> |