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>, tilemap_q: Query<( &TilemapSize, &TilemapGridSize, &TilemapTileSize, &TilemapAnchor, &TilemapType, &TileStorage, &GlobalTransform, &ViewVisibility, )>, mut output: MessageWriter, ) { 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::>(); output.write(PointerHits::new(*p_id, picks, camera.order as f32)); } }