leafwing_input_manager/
plugin.rs1use core::hash::Hash;
4use core::marker::PhantomData;
5use std::fmt::Debug;
6
7use bevy::app::{App, FixedPostUpdate, Plugin, RunFixedMainLoop};
8use bevy::input::InputSystems;
9#[cfg(feature = "picking")]
10use bevy::picking::PickingSystems;
11use bevy::prelude::*;
12use bevy::reflect::TypePath;
13use updating::CentralInputStore;
14
15use crate::Actionlike;
16use crate::action_state::{ActionState, ButtonData};
17use crate::clashing_inputs::ClashStrategy;
18use crate::input_map::InputMap;
19use crate::input_processing::*;
20use crate::prelude::updating::register_standard_input_kinds;
21#[cfg(feature = "timing")]
22use crate::timing::Timing;
23use crate::user_input::*;
24
25pub struct InputManagerPlugin<A: Actionlike> {
55 _phantom: PhantomData<A>,
56 machine: Machine,
57}
58
59impl<A: Actionlike> Default for InputManagerPlugin<A> {
61 fn default() -> Self {
62 Self {
63 _phantom: PhantomData,
64 machine: Machine::Client,
65 }
66 }
67}
68
69impl<A: Actionlike> InputManagerPlugin<A> {
70 #[must_use]
76 pub fn server() -> Self {
77 Self {
78 _phantom: PhantomData,
79 machine: Machine::Server,
80 }
81 }
82}
83
84enum Machine {
86 Server,
87 Client,
88}
89
90impl<A: Actionlike + TypePath + bevy::reflect::GetTypeRegistration> Plugin
91 for InputManagerPlugin<A>
92{
93 fn build(&self, app: &mut App) {
94 use crate::systems::*;
95
96 match self.machine {
97 Machine::Client => {
98 if !app.is_plugin_added::<CentralInputStorePlugin>() {
99 app.add_plugins(CentralInputStorePlugin);
100 }
101
102 app.add_systems(
104 PreUpdate,
105 (
106 tick_action_state::<A>.in_set(TickActionStateSystem::<A>::new()),
107 clear_central_input_store,
108 )
109 .in_set(InputManagerSystem::Tick)
110 .before(InputManagerSystem::Update),
111 )
112 .add_systems(PostUpdate, release_on_input_map_removed::<A>);
113
114 app.add_systems(
115 PreUpdate,
116 update_action_state::<A>.in_set(InputManagerSystem::Update),
117 );
118
119 app.configure_sets(
120 PreUpdate,
121 InputManagerSystem::ManualControl.after(InputManagerSystem::Update),
122 );
123
124 app.configure_sets(
125 PreUpdate,
126 InputManagerSystem::Unify
127 .after(InputManagerSystem::Filter)
128 .after(InputSystems),
129 );
130
131 app.configure_sets(
132 PreUpdate,
133 InputManagerSystem::Update
134 .after(InputSystems)
135 .after(InputManagerSystem::Unify),
136 );
137
138 #[cfg(feature = "picking")]
139 app.configure_sets(
140 PreUpdate,
141 InputManagerSystem::Update.before(PickingSystems::Hover),
142 );
143
144 app.add_systems(
146 RunFixedMainLoop,
147 (
148 swap_to_fixed_update::<A>,
149 update_action_state::<A>,
151 )
152 .chain()
153 .in_set(RunFixedMainLoopSystems::BeforeFixedMainLoop),
154 );
155
156 app.add_systems(FixedPostUpdate, release_on_input_map_removed::<A>);
157 app.add_systems(
158 FixedPostUpdate,
159 tick_action_state::<A>
160 .in_set(TickActionStateSystem::<A>::new())
161 .in_set(InputManagerSystem::Tick)
162 .before(InputManagerSystem::Update),
163 );
164 app.add_systems(
165 RunFixedMainLoop,
166 swap_to_update::<A>.in_set(RunFixedMainLoopSystems::AfterFixedMainLoop),
167 );
168 }
169 Machine::Server => {
170 app.add_systems(
171 PreUpdate,
172 tick_action_state::<A>
173 .in_set(TickActionStateSystem::<A>::new())
174 .in_set(InputManagerSystem::Tick),
175 );
176 }
177 };
178
179 #[cfg(feature = "mouse")]
180 app.register_buttonlike_input::<MouseButton>()
181 .register_buttonlike_input::<MouseMoveDirection>()
182 .register_buttonlike_input::<MouseButton>()
183 .register_axislike_input::<MouseMoveAxis>()
184 .register_dual_axislike_input::<MouseMove>()
185 .register_buttonlike_input::<MouseScrollDirection>()
186 .register_axislike_input::<MouseScrollAxis>()
187 .register_dual_axislike_input::<MouseScroll>();
188
189 #[cfg(feature = "keyboard")]
190 app.register_buttonlike_input::<KeyCode>()
191 .register_buttonlike_input::<ModifierKey>();
192
193 #[cfg(feature = "gamepad")]
194 app.register_buttonlike_input::<GamepadControlDirection>()
195 .register_axislike_input::<GamepadControlAxis>()
196 .register_dual_axislike_input::<GamepadStick>()
197 .register_buttonlike_input::<GamepadButton>();
198
199 app.register_axislike_input::<VirtualAxis>()
201 .register_dual_axislike_input::<VirtualDPad>()
202 .register_triple_axislike_input::<VirtualDPad3D>();
203
204 app.register_buttonlike_input::<ButtonlikeChord>()
206 .register_axislike_input::<AxislikeChord>()
207 .register_dual_axislike_input::<DualAxislikeChord>()
208 .register_triple_axislike_input::<TripleAxislikeChord>();
209
210 app.register_type::<ActionState<A>>()
212 .register_type::<InputMap<A>>()
213 .register_type::<ButtonData>()
214 .register_type::<ActionState<A>>()
215 .register_type::<CentralInputStore>();
216
217 app.register_type::<AxisProcessor>()
219 .register_type::<AxisBounds>()
220 .register_type::<AxisExclusion>()
221 .register_type::<AxisDeadZone>()
222 .register_type::<DualAxisProcessor>()
223 .register_type::<DualAxisInverted>()
224 .register_type::<DualAxisSensitivity>()
225 .register_type::<DualAxisBounds>()
226 .register_type::<DualAxisExclusion>()
227 .register_type::<DualAxisDeadZone>()
228 .register_type::<CircleBounds>()
229 .register_type::<CircleExclusion>()
230 .register_type::<CircleDeadZone>();
231
232 app.init_resource::<ClashStrategy>();
234
235 #[cfg(feature = "timing")]
236 app.register_type::<Timing>();
237 }
238}
239
240#[derive(SystemSet, Clone, Hash, Debug, PartialEq, Eq)]
244pub enum InputManagerSystem {
245 Tick,
249 Accumulate,
251 Filter,
253 Unify,
255 Update,
259 ManualControl,
263}
264
265#[derive(SystemSet, Clone, Hash, Debug, PartialEq, Eq)]
266pub struct TickActionStateSystem<A: Actionlike> {
268 phantom_data: PhantomData<A>,
269}
270
271impl<A: Actionlike> TickActionStateSystem<A> {
272 pub fn new() -> Self {
274 Self {
275 phantom_data: PhantomData,
276 }
277 }
278}
279
280impl<A: Actionlike> Default for TickActionStateSystem<A> {
281 fn default() -> Self {
282 Self::new()
283 }
284}
285
286pub struct CentralInputStorePlugin;
293
294impl Plugin for CentralInputStorePlugin {
295 fn build(&self, app: &mut App) {
296 app.insert_resource(CentralInputStore::default());
297
298 register_standard_input_kinds(app);
299
300 app.configure_sets(PreUpdate, InputManagerSystem::Unify.after(InputSystems));
301 }
302}