|
1 | 1 | <!-- Add mesh picking backend and `MeshRayCast` system parameter -->
|
2 | 2 | <!-- https://github.com/bevyengine/bevy/pull/15800 -->
|
3 | 3 |
|
4 |
| -<!-- TODO --> |
| 4 | + |
| 5 | + |
| 6 | +Being able to click on objects to select them is a vital and seemingly simple task in any game. |
| 7 | +Since 2020, doing this in Bevy has largely meant pulling in `@aevyrie`'s beloved ecosystem crate, [`bevy_mod_picking`] and its simple raycasting companion [`bevy_mod_raycast`]. |
| 8 | + |
| 9 | +Over the years, this crate has been refined and battle-tested, both by [Foresight Spatial Labs] (a CAD-creating, Bevy-using company where Aevyrie works) and |
| 10 | +the broader open source community of game developers that have used it for everything from first-person-shooters to point-and-click adventures. |
| 11 | +Bevy is thrilled to have had the chance to work with the team behind [`bevy_mod_picking`] and have adopted the project wholesale into Bevy itself. |
| 12 | +Integrating a large project is a ton of work, and we're incredibly grateful to the contributors who have made `bevy_picking` a stable, first-class feature of the engine. |
| 13 | + |
| 14 | +The new `bevy_picking` crate follows the existing modular architecture closely: |
| 15 | + |
| 16 | +1. Inputs are gathered from mouse, touch and pen devices. Each pointing device (humans are equipped with 10 by default) gets a screen-space [`PointerLocation`]. |
| 17 | +2. Each modular [backend] performs the domain-specific work (like raycasting) of figuring out how these pointer locations map to [`PointerHits`] on objects that they're watching. |
| 18 | +3. The hit information from each backend is combined and sorted to produce a coherent [`HoverMap`], which lists which entities each pointer is hovering over. |
| 19 | +4. High level events (both ordinary events and observers!) are emitted for each hovered entity, capturing complex behavior such as clicking, dragging or releasing various objects. |
| 20 | + |
| 21 | +In Bevy 0.15, we're shipping three first-party picking backends for UI, sprites, and meshes. Each of these comes with its own caveats for now: |
| 22 | + |
| 23 | +- UI: both the legacy [`Interaction`] and new [`PickingInteraction`] components exist [for now](https://github.com/bevyengine/bevy/issues/15550), with subtle behavioral differences. |
| 24 | +- Sprites: picking always uses the full rectangle, and [alpha transparency is not taken into account](https://github.com/bevyengine/bevy/issues/14929). |
| 25 | +- Mesh: this is a naive raycast against the full mesh. If you run into performance problems here, you should use simplified meshes and an acceleration data structure like a BVH to speed this up. As a result, this functionality is currently disabled by default. |
| 26 | + |
| 27 | +We expect both [`bevy_rapier`] and [`avian`] (the two most popular ecosystem physics crates for Bevy) to add their own accelerated collider picking backends to work with the newly upstreamed API. Unless you're debugging, building an editor or really care about the exact triangles of raw meshes, you should use one of those crates for efficient mesh picking. |
| 28 | + |
| 29 | +## Usage |
| 30 | + |
| 31 | +If you haven't used `bevy_picking`'s predecessor, there are two important and straightforward ways to get started with the API. |
| 32 | + |
| 33 | +First, you might want to quickly update the state of your objects (be they UI or game objects) based on what is being done to them, typically highlighting them or changing their color. For that, simply match against the [`PickingInteraction`] component. |
| 34 | + |
| 35 | +Secondly, you might want to respond dynamically to various pointer-powered events. For that, we recommend using observers (which replaced the existing `bevy_event_listener` solution during the upstreaming process). |
| 36 | +Here, we're spawning a simple text node and responding to pointer events. |
| 37 | + |
| 38 | +```rust |
| 39 | +use bevy::prelude::*; |
| 40 | + |
| 41 | +fn main() { |
| 42 | + App::new() |
| 43 | + .add_plugins((DefaultPlugins, MeshPickingPlugin)) |
| 44 | + .add_systems(Startup, setup_scene) |
| 45 | + .run(); |
| 46 | +} |
| 47 | + |
| 48 | +fn setup_scene( |
| 49 | + mut commands: Commands, |
| 50 | + mut meshes: ResMut<Assets<Mesh>>, |
| 51 | + mut materials: ResMut<Assets<StandardMaterial>>, |
| 52 | +) { |
| 53 | + // UI text that prints a message when clicked: |
| 54 | + commands |
| 55 | + .spawn(Text::new("Click Me!")) |
| 56 | + .observe(on_click_print_hello); |
| 57 | + |
| 58 | + // A cube that spins when dragged: |
| 59 | + commands |
| 60 | + .spawn(( |
| 61 | + Mesh3d(meshes.add(Cuboid::default())), |
| 62 | + MeshMaterial3d(materials.add(Color::WHITE)), |
| 63 | + )) |
| 64 | + // Picking observers work with *any* entity that has a picking backend running. |
| 65 | + // Try adding this `on_drag_spin` observer to the UI text! :) |
| 66 | + .observe(on_drag_spin); |
| 67 | + |
| 68 | + // Light and camera |
| 69 | + commands.spawn((PointLight::default(), Transform::from_xyz(4.0, 8.0, 4.0))); |
| 70 | + commands.spawn((Camera3d::default(), Transform::from_xyz(0.0, 2.0, 9.0))); |
| 71 | +} |
| 72 | + |
| 73 | +fn on_click_print_hello(click: Trigger<Pointer<Click>>) { |
| 74 | + println!("{} was clicked!", click.entity()); |
| 75 | +} |
| 76 | + |
| 77 | +fn on_drag_spin(drag: Trigger<Pointer<Drag>>, mut transforms: Query<&mut Transform>) { |
| 78 | + let mut transform = transforms.get_mut(drag.entity()).unwrap(); |
| 79 | + transform.rotate_y(drag.delta.x * 0.02); |
| 80 | +} |
| 81 | +``` |
| 82 | + |
| 83 | +If you want to override how an entity interacts with picking, add the [`PickingBehavior`] component to them and configure it to meet your needs. |
| 84 | + |
| 85 | +[`bevy_mod_picking`]: https://crates.io/crates/bevy_mod_picking/ |
| 86 | +[`bevy_mod_raycast`]: https://crates.io/crates/bevy_mod_raycast/ |
| 87 | +[Foresight Spatial Labs]: https://www.fslabs.ca/ |
| 88 | +[`PointerLocation`]: https://docs.rs/bevy/0.15.0/bevy/picking/backend/prelude/struct.PointerLocation.html |
| 89 | +[backend]: https://docs.rs/bevy/0.15.0/bevy/picking/backend/index.html |
| 90 | +[`PointerHits`]: https://docs.rs/bevy/0.15.0/bevy/picking/backend/struct.PointerHits.html |
| 91 | +[`HoverMap`]: https://docs.rs/bevy/0.15.0/bevy/picking/focus/struct.HoverMap.html |
| 92 | +[`Interaction`]: https://docs.rs/bevy/0.15.0/bevy/prelude/enum.Interaction.html |
| 93 | +[`PickingInteraction`]: https://docs.rs/bevy/0.15.0/bevy/picking/focus/enum.PickingInteraction.html |
| 94 | +[`bevy_rapier`]: https://crates.io/crates/bevy_rapier3d |
| 95 | +[`avian`]: https://crates.io/crates/avian3d |
| 96 | +[`PickingBehavior`]: https://docs.rs/bevy/0.15.0/bevy/picking/struct.PickingBehavior.html |
0 commit comments