use mevy::*; use bevy::{asset::embedded_asset, input::mouse::MouseWheel, math::{NormedVectorSpace, VectorSpace}, picking::{focus::HoverMap, pointer::PointerId}, prelude::*, utils::{dbg, HashMap}, window::PrimaryWindow}; use crate::map::{self, MapMarker}; pub struct Plugin; impl bevy::prelude::Plugin for Plugin { fn build(&self, app: &mut App) { app.add_systems(Startup, setup) .add_systems(Update, zoom_with_scroll); embedded_asset!(app, "../assets/ui/tree.png"); } } #[derive(Component, Debug)] struct PointersDragging(HashMap); #[derive(Component)] pub struct MapUIComponent; fn setup( mut world: Commands, asset_server: Res ) { spawn!{ Node {width: 100%, height: 100%, display: Display::Flex, flex_direction: FlexDirection::Column, !}; PickingBehavior { should_block_lower: false, is_hoverable: true }; .observe(move |trigger: Trigger>, mut ptrs: Query<&mut PointersDragging>| { if trigger.button == PointerButton::Primary { if let Ok(mut ptrs) = ptrs.get_mut(trigger.target) { ptrs.0.insert(trigger.pointer_id, trigger.pointer_location.position); } } }); .observe(move |trigger: Trigger>, mut ptrs: Query<&mut PointersDragging>| { if trigger.button == PointerButton::Primary { ptrs.single_mut().0.remove(&trigger.pointer_id); } }); .observe(| trigger: Trigger>, mut ptrs: Query<&mut PointersDragging>, mut cam: Query<&mut Transform, With>, | { if trigger.button == PointerButton::Primary { let mut ptrs = ptrs.single_mut(); if !ptrs.0.contains_key(&trigger.pointer_id) { return } let mut cam = cam.single_mut(); let old_midpoint = ptrs.0.values().fold(Vec2::ZERO, |acc, pos| acc + (pos/ptrs.0.len() as f32)); let old_d_to_midpoint = ptrs.0.values().fold(0., |acc, pos| acc + (old_midpoint-pos).norm()); ptrs.0.insert(trigger.pointer_id, trigger.pointer_location.position); let new_midpoint = ptrs.0.values().fold(Vec2::ZERO, |acc, pos| acc + (pos/ptrs.0.len() as f32)); let new_d_to_midpoint = ptrs.0.values().fold(0., |acc, pos| acc + (new_midpoint-pos).norm()); // move camera cam.translation.x -= (new_midpoint.x - old_midpoint.x)*cam.scale.x; cam.translation.y += (new_midpoint.y - old_midpoint.y)*cam.scale.y; if ptrs.0.len() > 1 { cam.scale *= old_d_to_midpoint/new_d_to_midpoint; } } } ); [map][ Node{ flex_grow: 1., !}; PointersDragging(HashMap::new()); MapUIComponent; PickingBehavior { should_block_lower: false, is_hoverable: true }; ] [ Node{ width: 100%, height: 10vh, align_self: AlignSelf::FlexEnd, !}; Button; BorderColor(#F00); BackgroundColor(#77767b); [ ImageNode::new(asset_server.load("embedded://forestiles/../assets/ui/tree.png")); Node { height: 80%, margin: [>1vh], !}; ] ] } // Spawn all ui elements as children of this one } fn zoom_with_scroll( mut cam: Query<&mut Transform, With>, mut ev_scroll: EventReader, hover_map: Res, window: Query<&Window, With>, map_ui_id: Query> ) { let map_ui_id = map_ui_id.single(); if hover_map.get(&PointerId::Mouse).and_then(|hovered_ids| hovered_ids.get(&map_ui_id)).is_some() { let window = window.single(); let mut cam = cam.single_mut(); for ev in ev_scroll.read() { let scale = (cam.scale.x-(ev.y*0.1/window.width().min(window.height()))).clamp(0.0001, 2./window.width().min(window.height())); cam.scale = Vec3::new(scale, scale, scale); } } }