@ -137,6 +137,7 @@ use core::marker::PhantomData;
use bevy ::{
use bevy ::{
ecs ::{ schedule ::ScheduleLabel , system ::ScheduleSystem } ,
ecs ::{ schedule ::ScheduleLabel , system ::ScheduleSystem } ,
input ::mouse ::AccumulatedMouseScroll ,
platform ::collections ::{ HashMap , HashSet } ,
platform ::collections ::{ HashMap , HashSet } ,
prelude ::* ,
prelude ::* ,
ui ::Checked ,
ui ::Checked ,
@ -258,8 +259,8 @@ pub trait GameAction: Copy + Eq + Hash + Send + Sync + 'static {
///
///
/// [`Key`](Self::Key), [`Mouse`](Self::Mouse) and [`Gamepad`](Self::Gamepad) are
/// [`Key`](Self::Key), [`Mouse`](Self::Mouse) and [`Gamepad`](Self::Gamepad) are
/// digital (for [`Instant`](ActionKind::Instant)/[`Toggle`](ActionKind::Toggle)
/// digital (for [`Instant`](ActionKind::Instant)/[`Toggle`](ActionKind::Toggle)
/// actions); [`Stick`](Self::Stick) is analog (for [`Axis`](ActionKind::Axis)
/// actions); [`Stick`](Self::Stick) and [`Scroll`](Self::Scroll) are analog (for
/// actions).
/// [`Axis`](ActionKind::Axis) actions).
#[ derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Reflect) ]
#[ derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Reflect) ]
pub enum Binding {
pub enum Binding {
/// A keyboard key.
/// A keyboard key.
@ -271,6 +272,12 @@ pub enum Binding {
/// A gamepad analog source producing a [`Vec2`] (matched across all connected
/// A gamepad analog source producing a [`Vec2`] (matched across all connected
/// gamepads; deadzone already applied by Bevy).
/// gamepads; deadzone already applied by Bevy).
Stick ( Stick ) ,
Stick ( Stick ) ,
/// The mouse wheel, producing this frame's scroll delta as a [`Vec2`]
/// (`x` = horizontal, `y` = vertical). Like a stick, it drives
/// [`Axis`](ActionKind::Axis) actions — but it is a *delta* (non-zero only
/// while scrolling), so the axis reads zero on idle frames and
/// [`on_action`] fires exactly while the wheel turns.
Scroll ,
}
}
/// An analog 2D source on a gamepad.
/// An analog 2D source on a gamepad.
@ -557,28 +564,39 @@ fn hardware_to_actions<T: GameAction>(
keys : Res < ButtonInput < KeyCode > > ,
keys : Res < ButtonInput < KeyCode > > ,
mouse : Res < ButtonInput < MouseButton > > ,
mouse : Res < ButtonInput < MouseButton > > ,
pads : Query < & Gamepad > ,
pads : Query < & Gamepad > ,
scroll : Res < AccumulatedMouseScroll > ,
map : Res < InputMap < T > > ,
map : Res < InputMap < T > > ,
mut state : ResMut < ActionState < T > > ,
mut state : ResMut < ActionState < T > > ,
) {
) {
for ( & action , bindings ) in & map . bindings {
for ( & action , bindings ) in & map . bindings {
match action . kind ( ) {
match action . kind ( ) {
// Analog: pick the most-deflected reading across sticks/gamepads and
// Analog: pick the strongest reading across analog sources and store it
// store it every frame (zero when nothing is connected or centered ).
// every frame (zero when nothing is deflected / not scrolling ).
ActionKind ::Axis = > {
ActionKind ::Axis = > {
let mut value = Vec2 ::ZERO ;
let mut value = Vec2 ::ZERO ;
for binding in bindings {
for binding in bindings {
let Binding ::Stick ( stick ) = binding else {
let reading = match binding {
continue ;
Binding ::Stick ( stick ) = > {
} ;
// Best deflection across all connected gamepads.
for pad in & pads {
let mut best = Vec2 ::ZERO ;
let reading = match stick {
for pad in & pads {
Stick ::Left = > pad . left_stick ( ) ,
let r = match stick {
Stick ::Right = > pad . right_stick ( ) ,
Stick ::Left = > pad . left_stick ( ) ,
Stick ::DPad = > pad . dpad ( ) ,
Stick ::Right = > pad . right_stick ( ) ,
} ;
Stick ::DPad = > pad . dpad ( ) ,
if reading . length_squared ( ) > value . length_squared ( ) {
} ;
value = reading ;
if r . length_squared ( ) > best . length_squared ( ) {
best = r ;
}
}
best
}
}
// This frame's wheel delta (zero when not scrolling).
Binding ::Scroll = > scroll . delta ,
_ = > continue ,
} ;
if reading . length_squared ( ) > value . length_squared ( ) {
value = reading ;
}
}
}
}
state . set_axis ( action , value ) ;
state . set_axis ( action , value ) ;
@ -591,7 +609,8 @@ fn hardware_to_actions<T: GameAction>(
Binding ::Key ( k ) = > keys . just_pressed ( * k ) ,
Binding ::Key ( k ) = > keys . just_pressed ( * k ) ,
Binding ::Mouse ( m ) = > mouse . just_pressed ( * m ) ,
Binding ::Mouse ( m ) = > mouse . just_pressed ( * m ) ,
Binding ::Gamepad ( g ) = > pads . iter ( ) . any ( | pad | pad . just_pressed ( * g ) ) ,
Binding ::Gamepad ( g ) = > pads . iter ( ) . any ( | pad | pad . just_pressed ( * g ) ) ,
Binding ::Stick ( _ ) = > false , // not meaningful for digital actions
// Analog sources are not meaningful for digital actions.
Binding ::Stick ( _ ) | Binding ::Scroll = > false ,
} ;
} ;
if just_pressed {
if just_pressed {
match kind {
match kind {