Leafwing-Studios / leafwing-input-manager

A straightforward stateful input manager for the Bevy game engine.
Apache License 2.0
612 stars 91 forks source link

Input is missed if the underlying ButtonInput only lasts for a single frame #527

Open Indeximal opened 1 month ago

Indeximal commented 1 month ago

Relevant system information

Description

Using leafwing-input-manager for mouse click input, a significant fraction of my trackpad taps get ignored. I.e. the just_pressed() function seems to never return true for some clicks. This does not happen when I physically click the touchpad.

I believe this is due to the fact that the tap creates a press and release event simultaneously, ie just_pressed and just_released of bevy_input are true in the same frame, as the following logs show. Therefore I assume you clear the pressed state again before any schedule has time to read it. For the sake of the user experience I think it would be nicer if the pressed state would be forced to last for at least one frame/update.

Additional information / logs

I ran the following system:


fn log_clicks(
    frame: Res<bevy::core::FrameCount>,
    leafwing: Res<ActionState<Action>>,
    bevy: Res<ButtonInput<MouseButton>>,
) {
    if leafwing.just_pressed(&Action::Build) {
        debug!("{frame:?} leafwing click");
    }
    if leafwing.just_released(&Action::Build) {
        debug!("{frame:?} leafwing release");
    }
    if bevy.just_pressed(MouseButton::Left) {
        debug!("{frame:?} bevy click");
    }
    if bevy.just_released(MouseButton::Left) {
        debug!("{frame:?} bevy release");
    }
}

with the Action::Build being mapped to (Self::Build, Single(Mouse(MouseButton::Left)))

And got the following (shortened) output:

// those are 3 normal clicks
..:43.767761Z: Res(FrameCount(486)) leafwing click
..:43.767798Z: Res(FrameCount(486)) bevy click
..:44.019270Z: Res(FrameCount(501)) leafwing release
..:44.019294Z: Res(FrameCount(501)) bevy release
..:50.605571Z: Res(FrameCount(835)) leafwing click
..:50.605597Z: Res(FrameCount(835)) bevy click
..:50.885160Z: Res(FrameCount(852)) leafwing release
..:50.885212Z: Res(FrameCount(852)) bevy release
..:54.312468Z: Res(FrameCount(1057)) leafwing click
..:54.312493Z: Res(FrameCount(1057)) bevy click
..:54.649199Z: Res(FrameCount(1077)) leafwing release
..:54.649222Z: Res(FrameCount(1077)) bevy release
// here I switched to tapping
..:00.345182Z: Res(FrameCount(1418)) bevy click
..:00.345213Z: Res(FrameCount(1418)) bevy release
..:02.616926Z: Res(FrameCount(1554)) leafwing click
..:02.617046Z: Res(FrameCount(1554)) bevy click
..:02.634939Z: Res(FrameCount(1555)) leafwing release
..:02.634971Z: Res(FrameCount(1555)) bevy release
..:05.693888Z: Res(FrameCount(1738)) bevy click
..:05.693927Z: Res(FrameCount(1738)) bevy release
..:08.082196Z: Res(FrameCount(1881)) bevy click
..:08.082226Z: Res(FrameCount(1881)) bevy release
..:10.588752Z: Res(FrameCount(2031)) bevy click
..:10.588776Z: Res(FrameCount(2031)) bevy release
..:12.744516Z: Res(FrameCount(2160)) leafwing click
..:12.744539Z: Res(FrameCount(2160)) bevy click
..:12.762709Z: Res(FrameCount(2161)) leafwing release
..:12.762734Z: Res(FrameCount(2161)) bevy release
..:15.436291Z: Res(FrameCount(2321)) bevy click
..:15.436315Z: Res(FrameCount(2321)) bevy release
..:17.875363Z: Res(FrameCount(2467)) leafwing click
..:17.875386Z: Res(FrameCount(2467)) bevy click
..:17.893768Z: Res(FrameCount(2468)) leafwing release
..:17.893796Z: Res(FrameCount(2468)) bevy release

Even more information

I have also run the pump_events example of winit, which logs all events every 16ms and got interesting results:

Sometimes when I tap, no Update or RedrawRequested event are logged between the pressed and released:

RedrawRequested
Update()
RedrawRequested
Update()
CursorMoved { device_id: DeviceId(DeviceId), position: PhysicalPosition { x: 351.29827880859375, y: 194.4062042236328 } }
MouseInput { device_id: DeviceId(DeviceId), state: Pressed, button: Left }
CursorMoved { device_id: DeviceId(DeviceId), position: PhysicalPosition { x: 351.29827880859375, y: 194.40618896484375 } }
MouseInput { device_id: DeviceId(DeviceId), state: Released, button: Left }
RedrawRequested
Update()
RedrawRequested
Update()

Sometimes there is a single update between the two tap events:

Update()
RedrawRequested
Update()
CursorMoved { device_id: DeviceId(DeviceId), position: PhysicalPosition { x: 351.29827880859375, y: 194.4062042236328 } }
MouseInput { device_id: DeviceId(DeviceId), state: Pressed, button: Left }
RedrawRequested
Update()
CursorMoved { device_id: DeviceId(DeviceId), position: PhysicalPosition { x: 351.29827880859375, y: 194.40618896484375 } }
MouseInput { device_id: DeviceId(DeviceId), state: Released, button: Left }
RedrawRequested

And for comparison, here is how it looks when I casually press the touchpad instead of tapping.

Update()
RedrawRequested
Update()
CursorMoved { device_id: DeviceId(DeviceId), position: PhysicalPosition { x: 351.29827880859375, y: 194.4062042236328 } }
MouseInput { device_id: DeviceId(DeviceId), state: Pressed, button: Left }
RedrawRequested
Update()
RedrawRequested
Update()
RedrawRequested
Update()
RedrawRequested
Update()
RedrawRequested
Update()
RedrawRequested
Update()
RedrawRequested
Update()
CursorMoved { device_id: DeviceId(DeviceId), position: PhysicalPosition { x: 351.29827880859375, y: 194.40618896484375 } }
MouseInput { device_id: DeviceId(DeviceId), state: Released, button: Left }
RedrawRequested
Update()
RedrawRequested
alice-i-cecile commented 1 month ago

Agreed, I think this should be considered a bug.