[no description]
Diffstat (limited to 'src/picking.rs')
| -rw-r--r-- | src/picking.rs | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/src/picking.rs b/src/picking.rs new file mode 100644 index 0000000..282769c --- /dev/null +++ b/src/picking.rs @@ -0,0 +1,106 @@ +use bevy::camera::RenderTarget; +use bevy::picking::PickingSystems; +use bevy::picking::backend::HitData; +use bevy::picking::backend::PointerHits; +use bevy::picking::pointer::PointerId; +use bevy::picking::pointer::PointerLocation; +use bevy::prelude::*; +use bevy::window::PrimaryWindow; +use bevy_ecs_tilemap::prelude::*; + +pub struct TilemapPickingPlugin; + +impl Plugin for TilemapPickingPlugin { + fn build(&self, app: &mut App) { + app.add_systems(PreUpdate, tile_picking.in_set(PickingSystems::Backend)); + } +} + +fn tile_picking( + pointers_q: Query<(&PointerId, &PointerLocation)>, + cameras_q: Query<( + Entity, + &Camera, + &RenderTarget, + &GlobalTransform, + &Projection, + )>, + primary_window_q: Query<Entity, With<PrimaryWindow>>, + tilemap_q: Query<( + &TilemapSize, + &TilemapGridSize, + &TilemapTileSize, + &TilemapAnchor, + &TilemapType, + &TileStorage, + &GlobalTransform, + &ViewVisibility, + )>, + mut output: MessageWriter<PointerHits>, +) { + for (p_id, p_loc) in pointers_q + .iter() + .filter_map(|(p_id, p_loc)| p_loc.location().map(|l| (p_id, l))) + { + let Some((cam_entity, camera, _cam_target, cam_transform, _cam_ortho)) = cameras_q + .iter() + .filter(|(_, camera, ..)| camera.is_active) + .find(|(_, _, cam_target, ..)| { + cam_target + .normalize(match primary_window_q.single() { + Ok(w) => Some(w), + Err(_) => return false, + }) + .as_ref() + == Some(&p_loc.target) + }) + else { + continue; + }; + + let Ok(cursor_pos_world) = camera.viewport_to_world_2d(cam_transform, p_loc.position) + else { + continue; + }; + + let picks = tilemap_q + .iter() + .filter(|(.., vis)| vis.get()) + .filter_map( + |( + map_s, + map_grid_size, + map_tile_size, + map_anchor, + map_type, + map_store, + map_transform, + .., + )| { + let in_map_pos = { + let pos = Vec4::from((cursor_pos_world, 0., 1.0)); + let in_map_pos = map_transform.to_matrix().inverse() * pos; + in_map_pos.xy() + }; + + let tile_entity = TilePos::from_world_pos( + &in_map_pos, + map_s, + map_grid_size, + map_tile_size, + map_type, + map_anchor, + ) + .and_then(|tile_pos| map_store.get(&tile_pos))?; + + Some(( + tile_entity, + HitData::new(cam_entity, -map_transform.translation().z, None, None), + )) + }, + ) + .collect::<Vec<_>>(); + + output.write(PointerHits::new(*p_id, picks, camera.order as f32)); + } +} |