bevy_enhanced_input/binding/
relationship.rs

1use alloc::slice;
2use core::iter::Copied;
3
4use bevy::{
5    ecs::relationship::{RelatedSpawner, RelatedSpawnerCommands},
6    prelude::*,
7};
8#[cfg(feature = "serialize")]
9use serde::{Deserialize, Serialize};
10
11use crate::prelude::*;
12
13/// Action entity associated with this binding entity.
14///
15/// See also the [`bindings!`](crate::prelude::bindings) macro for conveniently spawning associated actions.
16#[derive(Component, Deref, Reflect, Debug, PartialEq, Eq, Clone)]
17#[relationship(relationship_target = Bindings)]
18#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
19#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
20pub struct BindingOf(pub Entity);
21
22/// Binding entities associated with this action entity.
23///
24/// See also the [`bindings!`](crate::prelude::bindings) macro for conveniently spawning associated actions.
25#[derive(Component, Deref, Reflect, Debug, Default, PartialEq, Eq)]
26#[relationship_target(relationship = BindingOf, linked_spawn)]
27pub struct Bindings(Vec<Entity>);
28
29impl<'a> IntoIterator for &'a Bindings {
30    type Item = Entity;
31    type IntoIter = Copied<slice::Iter<'a, Entity>>;
32
33    fn into_iter(self) -> Self::IntoIter {
34        self.iter()
35    }
36}
37
38/// A type alias over [`RelatedSpawner`] used to spawn binding entities containing a [`BindingOf`] relationship.
39pub type BindingSpawner<'w> = RelatedSpawner<'w, BindingOf>;
40
41/// A type alias over [`RelatedSpawnerCommands`] used to spawn binding entities containing a [`BindingOf`] relationship.
42pub type BindingSpawnerCommands<'w> = RelatedSpawnerCommands<'w, BindingOf>;
43
44/// Returns a [`SpawnRelatedBundle`](bevy::ecs::spawn::SpawnRelatedBundle) that will insert the [`Bindings`] component and
45/// spawn a [`SpawnableList`] of entities with given bundles that relate to the context entity via the
46/// [`BindingOf`] component.
47///
48/// Similar to [`related!`], but allows you to omit the explicit [`Bindings`] type and automatically wraps elements using
49/// [`Binding::from`](crate::prelude::Binding::from).
50///
51/// The macro accepts either individual elements that implement [`Into<Binding>`], or tuples where the first element implements
52/// [`Into<Binding>`] and the remaining elements are bundles.
53///
54/// The macro can't be used to spawn [presets](crate::preset). See the module documentation for more details.
55///
56/// See also [`actions!`](crate::prelude::actions).
57///
58/// # Examples
59///
60/// A list of action bindings with components constructed from values that implement [`Into<Binding>`].
61///
62/// ```
63/// # use bevy::prelude::*;
64/// # use bevy_enhanced_input::prelude::*;
65/// # let mut world = World::new();
66/// world.spawn(bindings![KeyCode::Space, GamepadButton::South]);
67/// # assert_eq!(world.entities().len(), 3);
68/// ```
69///
70/// A single action binding with the first component constructed from a value implementing [`Into<Binding>`],
71/// and the rest as regular components.
72///
73/// ```
74/// # use bevy::prelude::*;
75/// # use bevy_enhanced_input::prelude::*;
76/// # let mut world = World::new();
77/// world.spawn(bindings![(
78///     GamepadButton::RightTrigger2,
79///     Down::new(0.3),
80/// )]);
81/// # assert_eq!(world.entities().len(), 2);
82/// ```
83///
84/// A list of action bindings with the first component constructed from a value implementing [`Into<Binding>`],
85/// and the rest as regular components.
86///
87/// ```
88/// # use bevy::prelude::*;
89/// # use bevy_enhanced_input::prelude::*;
90/// # let mut world = World::new();
91/// world.spawn(bindings![
92///     (GamepadButton::RightTrigger2, Down::new(0.3)),
93///     MouseButton::Left,
94/// ]);
95/// # assert_eq!(world.entities().len(), 3);
96/// ```
97///
98/// [`SpawnableList`]: bevy::ecs::spawn::SpawnableList
99#[macro_export]
100macro_rules! bindings {
101    [$($binding:expr),*$(,)?] => {
102        ::bevy::prelude::related!($crate::prelude::Bindings[$($crate::prelude::IntoBindingBundle::into_binding_bundle($binding)),*])
103    };
104}
105
106/// Types that can be converted into a bundle whose first element can be converted into a [`Binding`].
107///
108/// Used to avoid writing [`Binding::from`] inside [`bindings!`].
109pub trait IntoBindingBundle {
110    /// Returns a bundle for a binding.
111    fn into_binding_bundle(self) -> impl Bundle;
112}
113
114impl<B: Into<Binding>> IntoBindingBundle for B {
115    fn into_binding_bundle(self) -> impl Bundle {
116        self.into()
117    }
118}
119
120macro_rules! impl_into_binding_bundle {
121    ($($C:ident),*) => {
122        impl<B: Into<Binding>, $($C: Bundle,)*> IntoBindingBundle for (B, $($C),*) {
123            #[allow(non_snake_case, reason = "tuple unpack")]
124            fn into_binding_bundle(self) -> impl Bundle {
125                let (b, $($C),* ) = self;
126                (b.into(), $($C),*)
127            }
128        }
129    }
130}
131
132variadics_please::all_tuples!(impl_into_binding_bundle, 0, 14, C);