use bevy::{ asset::embedded_asset, input::mouse::MouseWheel, math::NormedVectorSpace, picking::{focus::HoverMap, pointer::PointerId}, prelude::*, utils::HashMap, window::PrimaryWindow, }; use mevy::*; use crate::camera::CameraMarker; use crate::map::{AnimalKind, CellKind}; // #77767b const TABBAR_COLOR: Color = Color::srgb(119. / 255., 118. / 255., 123. / 255.); // #E8E8E8 const ENABLED_BUTTON_COLOR: Color = Color::srgb(232. / 255., 232. / 255., 232. / 255.); pub struct Plugin; impl bevy::prelude::Plugin for Plugin { fn build(&self, app: &mut App) { app.init_resource::() .add_systems(Startup, setup) .add_systems(Update, zoom_with_scroll); embedded_asset!(app, "../assets/ui/enabled_tree.png"); embedded_asset!(app, "../assets/ui/disabled_tree.png"); embedded_asset!(app, "../assets/ui/enabled_grass.png"); embedded_asset!(app, "../assets/ui/disabled_grass.png"); embedded_asset!(app, "../assets/ui/enabled_cross.png"); embedded_asset!(app, "../assets/ui/disabled_cross.png"); embedded_asset!(app, "../assets/ui/enabled_goat.png"); embedded_asset!(app, "../assets/ui/disabled_goat.png"); } } #[derive(Resource, Default, PartialEq, Eq)] pub enum CurrentAction { #[default] None, ChangeCell(CellKind), AddAnimal(AnimalKind), } #[derive(Component, Debug)] struct PointersDragging(HashMap); #[derive(Component)] pub struct MapUIComponent; fn setup(mut world: Commands, asset_server: Res) { // Spawn all ui elements as children of this one spawn! { Node {width: 100%, height: 100%, display: Display::Flex, flex_direction: FlexDirection::Column, !}; PickingBehavior { should_block_lower: false, is_hoverable: true }; .observe(|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.translation.z*0.001; cam.translation.y += (new_midpoint.y - old_midpoint.y)*cam.translation.z*0.001; if ptrs.0.len() > 1 { let forward = cam.forward(); let z = cam.translation.z; cam.translation += forward * (z * (1. - (new_d_to_midpoint/old_d_to_midpoint)) / forward.z); // 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 }; .observe(|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); } } }); ] [ Node{ width: 100%, height: 10vh, align_self: AlignSelf::FlexEnd, !}; Button; BackgroundColor(TABBAR_COLOR); [forest][ ImageNode::new(asset_server.load("embedded://forestiles/../assets/ui/enabled_tree.png")); Node { // height: 80%, // margin: [>1vh], !}; BackgroundColor(TABBAR_COLOR); .observe(move |trigger: Trigger>, mut ca: ResMut, mut bg: Query<&mut BackgroundColor>| { if trigger.button == PointerButton::Primary { if *ca == CurrentAction::ChangeCell(CellKind::Forest) { *ca = CurrentAction::None; bg.get_mut(forest).unwrap().0 = TABBAR_COLOR; } else { *ca = CurrentAction::ChangeCell(CellKind::Forest); bg.get_mut(forest).unwrap().0 = ENABLED_BUTTON_COLOR; } bg.get_mut(grass).unwrap().0 = TABBAR_COLOR; bg.get_mut(goat).unwrap().0 = TABBAR_COLOR; } }); ] [grass][ ImageNode::new(asset_server.load("embedded://forestiles/../assets/ui/enabled_grass.png")); Node { // height: 80%, // margin: [>1vh], !}; BackgroundColor(TABBAR_COLOR); .observe(move |trigger: Trigger>, mut ca: ResMut, mut bg: Query<&mut BackgroundColor>| { if trigger.button == PointerButton::Primary { if *ca == CurrentAction::ChangeCell(CellKind::Grass) { *ca = CurrentAction::None; bg.get_mut(grass).unwrap().0 = TABBAR_COLOR; } else { *ca = CurrentAction::ChangeCell(CellKind::Grass); bg.get_mut(grass).unwrap().0 = ENABLED_BUTTON_COLOR; } bg.get_mut(forest).unwrap().0 = TABBAR_COLOR; bg.get_mut(goat).unwrap().0 = TABBAR_COLOR; } }); ] [goat][ ImageNode::new(asset_server.load("embedded://forestiles/../assets/ui/enabled_goat.png")); Node { // height: 80%, // margin: [>1vh], !}; BackgroundColor(TABBAR_COLOR); .observe(move |trigger: Trigger>, mut ca: ResMut, mut bg: Query<&mut BackgroundColor>| { if trigger.button == PointerButton::Primary { if *ca == CurrentAction::AddAnimal(AnimalKind::Goat) { *ca = CurrentAction::None; bg.get_mut(goat).unwrap().0 = TABBAR_COLOR; } else { *ca = CurrentAction::AddAnimal(AnimalKind::Goat); bg.get_mut(goat).unwrap().0 = ENABLED_BUTTON_COLOR; } bg.get_mut(forest).unwrap().0 = TABBAR_COLOR; bg.get_mut(grass).unwrap().0 = TABBAR_COLOR; } }); ] ] } } 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 forward = cam.forward(); cam.translation += forward * (ev.y * 0.1); // cam.fov = cam.fov + (ev.y * 0.1); // let scale = (cam.scale.x - (ev.y * 0.1 / window.width().min(window.height()))) // .clamp(0.0001, 2. / window.width().min(window.height())); } } }