1use bevy::{prelude::*, reflect::TypePath, ui::UiSystem};
45use std::{hash::Hash, marker::PhantomData};
46
47mod behavior;
48#[cfg(feature = "gamepad_mapping")]
49mod gamepad;
50mod input;
51mod ui;
52
53pub mod prelude {
55 #[cfg(feature = "gamepad_mapping")]
56 pub use crate::TouchStickGamepadMapping;
57 pub use crate::{TouchStick, TouchStickPlugin, TouchStickType, TouchStickUiBundle};
58}
59
60#[cfg(feature = "gamepad_mapping")]
61use crate::gamepad::GamepadMappingPlugin;
62#[cfg(feature = "gamepad_mapping")]
63pub use crate::gamepad::TouchStickGamepadMapping;
64
65pub use crate::{
66 behavior::TouchStickType,
67 ui::{TouchStickInteractionArea, TouchStickUiBundle, TouchStickUiKnob, TouchStickUiOutline},
68};
69use crate::{
70 input::{
71 send_drag_events_from_mouse, send_drag_events_from_touch, update_sticks_from_drag_events,
72 DragEvent,
73 },
74 ui::TouchStickUiPlugin,
75};
76
77#[derive(Component, Clone, Debug, Reflect)]
79#[reflect(Component, Default)]
80pub struct TouchStick<S: StickIdType> {
81 pub id: S,
83 pub drag_id: Option<u64>,
85 pub dead_zone: f32,
87 pub base_position: Vec2,
91 pub drag_start: Vec2,
93 pub drag_position: Vec2,
95 pub value: Vec2,
97 pub interactable_zone: Rect,
99 pub radius: f32,
101 pub stick_type: TouchStickType,
103}
104
105impl<S: StickIdType> Default for TouchStick<S> {
106 fn default() -> Self {
107 Self {
108 id: default(),
109 drag_id: None,
110 dead_zone: 0.,
111 base_position: default(),
112 drag_start: default(),
113 drag_position: default(),
114 value: default(),
115 interactable_zone: Rect {
116 min: Vec2::MIN,
117 max: Vec2::MAX,
118 },
119 radius: 75.,
120 stick_type: default(),
121 }
122 }
123}
124
125impl<S: StickIdType> From<S> for TouchStick<S> {
126 fn from(id: S) -> Self {
127 Self::new(id)
128 }
129}
130
131impl<S: StickIdType> TouchStick<S> {
132 pub fn new(id: S) -> Self {
134 Self { id, ..default() }
135 }
136}
137
138pub struct TouchStickPlugin<S> {
140 _marker: PhantomData<S>,
141}
142
143impl<S> Default for TouchStickPlugin<S> {
144 fn default() -> Self {
145 Self { _marker: default() }
146 }
147}
148
149impl<S: StickIdType> Plugin for TouchStickPlugin<S> {
150 fn build(&self, app: &mut bevy::prelude::App) {
151 app.register_type::<TouchStickInteractionArea>()
152 .register_type::<TouchStick<S>>()
153 .register_type::<TouchStickType>()
154 .register_type::<TouchStickEventType>()
155 .add_event::<TouchStickEvent<S>>()
156 .add_event::<DragEvent>()
157 .add_plugins(TouchStickUiPlugin::<S>::default())
158 .add_systems(
159 PreUpdate,
160 (
161 send_drag_events_from_touch.before(update_sticks_from_drag_events::<S>),
163 send_drag_events_from_mouse.before(update_sticks_from_drag_events::<S>),
164 ),
165 )
166 .add_systems(PreUpdate, update_sticks_from_drag_events::<S>)
167 .add_systems(
168 PostUpdate,
169 map_input_zones_from_ui_nodes::<S>.before(UiSystem::Layout),
170 );
171
172 #[cfg(feature = "gamepad_mapping")]
173 app.add_plugins(GamepadMappingPlugin::<S>::default());
174 }
175}
176
177pub trait StickIdType:
179 Hash + Sync + Send + Clone + Default + Reflect + FromReflect + TypePath + 'static
180{
181}
182
183impl<S: Hash + Sync + Send + Clone + Default + Reflect + FromReflect + TypePath + 'static>
184 StickIdType for S
185{
186}
187
188fn map_input_zones_from_ui_nodes<S: StickIdType>(
189 mut interaction_areas: Query<
190 (&mut TouchStick<S>, &GlobalTransform, &Node),
191 With<TouchStickInteractionArea>,
192 >,
193) {
194 for (mut touch_stick, transform, node) in &mut interaction_areas {
195 let pos = transform.translation().truncate();
196 let size = node.size();
197 let interaction_area = Rect::from_center_size(pos, size);
198 touch_stick.interactable_zone = interaction_area;
199 }
200}
201
202#[derive(Clone, Copy, Debug, PartialEq, Eq, Reflect)]
204#[reflect]
205pub enum TouchStickEventType {
206 Press,
208 Drag,
210 Release,
212}
213
214#[derive(Event)]
216pub struct TouchStickEvent<S: StickIdType> {
217 id: S,
219 event: TouchStickEventType,
221 value: Vec2,
223}
224
225impl<S: StickIdType> TouchStickEvent<S> {
226 pub fn id(&self) -> S {
228 self.id.clone()
229 }
230
231 pub fn value(&self) -> Vec2 {
233 self.value
234 }
235
236 pub fn get_type(&self) -> TouchStickEventType {
238 self.event
239 }
240}