Drag and DragEnd system ordering inconsistencies
Context
I want to implement a drag and drop, when dropping, I want to "snap" the position.
bug description
Sometimes, the snapping occurs incorrectly. this bug doesn't happen on all sessions, but is easily reproducible after a few relaunches.
DragEnd event sometimes occurs before Drag events: so logic in DragEnd gets overridden by logic in Drag.
Video description
https://github.com/aevyrie/bevy_mod_picking/assets/2290685/5302bd08-95a4-407b-83e6-e90f8ee3ae38
More details
part of relevant code
fn round_to_nearest(value: f32) -> f32 {
(value / multiple).round() * 50f32
}
...
// handle drag
On::<Pointer<Drag>>::listener_component_mut::<Transform>(|drag, transform| {
transform.translation.x += drag.delta.x;
transform.translation.y -= drag.delta.y;
}),
On::<Pointer<DragEnd>>::run(|event: ListenerMut<Pointer<DragEnd>>,
mut transforms: Query<&mut Transform>,| {
let Ok(mut transform) = transforms.get_mut(event.listener()) else {
return;
};
// snap to position
transform.translation.x = round_to_nearest(transform.translation.x, 60f32); // Make the square follow the mouse
transform.translation.y = round_to_nearest(transform.translation.y, 60f32);
// re-enable picking
commands.entity(event.target()).insert(Pickable::default());
})
Trace image
Not sure why there are 2 drag events on the same frame too 🤔
full tracy trace file: drag_trace.zip
Code source:https://github.com/Vrixyz/rsword/blob/8fbe5713856d971dc2e71eaad4ac200085314dfb/src/game.rs#L121
Thoughts on fix
I looked a tiny bit into how we could fix that, and I guess we could avoid sending a Drag when we detect a DragEnd ?
Around https://github.com/Vrixyz/bevy_mod_picking/blob/1471a9f83435f3fdd43829b90391e6ab965e0e73/crates/bevy_picking_core/src/events.rs#L417-L418
Workaround
As a workaround, in user code I fire an event when a DragEnd is detected, so its logic is computed after last Drag inputs.
Code Screenshot
This is caused by bevy's lack of inter-frame event ordering. There is no way to know if a mouse up came before or after a mouse move, or a mouse down before or after a mouse move within the same frame. Additionally, all the events are split into independent events. Once the bevy issue is fixed, the plugin will also need to switch to a unified pointer event stream.