[no description]
Diffstat (limited to 'src/picking.rs')
-rw-r--r--src/picking.rs106
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));
+ }
+}