bevy_ui_3d/
lib.rs

1use bevy::prelude::*;
2use bevy_rapier3d::prelude::*;
3
4use smallvec::SmallVec;
5
6mod ui_3d;
7
8pub use ui_3d::{Interaction3d, PluginConfig};
9
10#[derive(Component, Default)]
11pub struct Ui3dElement;
12
13/// Add this bundle to 3d objects you create that you want to support interactions on
14/// Alternatively, just add the components separately
15#[derive(Bundle, Default)]
16pub struct Ui3dElementBundle {
17    pub ui_element: Ui3dElement,
18    pub interaction: Interaction3d,
19    pub collider: Collider,
20}
21
22#[derive(Default, Resource)]
23pub(crate) struct UiState {
24    /// Contains entities whose Interaction should be set to None
25    ui_3d_entities_to_reset: SmallVec<[Entity; 1]>,
26}
27
28#[derive(Default)]
29pub struct Ui3dPlugin {
30    pub config: PluginConfig,
31}
32
33impl Plugin for Ui3dPlugin {
34    fn build(&self, app: &mut App) {
35        app.init_resource::<UiState>()
36            .insert_resource(self.config.clone());
37
38        if !app.is_plugin_added::<RapierPhysicsPlugin>() {
39            app.add_plugins(RapierPhysicsPlugin::<NoUserData>::default());
40        }
41
42        app.add_systems(
43            PreUpdate,
44            (absorb_bevy_ui_inputs).after(bevy::ui::ui_focus_system),
45        );
46
47        app.add_systems(Update, ui_3d::interaction_system);
48    }
49}
50
51fn absorb_bevy_ui_inputs(
52    mut mouse: ResMut<Input<MouseButton>>,
53    interaction_query: Query<&Interaction, (With<Node>, Changed<Interaction>)>,
54) {
55    let event_absorbed_by_ui = interaction_query
56        .iter()
57        .any(|i| matches!(i, Interaction::Pressed | Interaction::Hovered));
58
59    if event_absorbed_by_ui {
60        mouse.reset_all();
61    }
62}