Compare commits
8 Commits
5f3c0e1b36
...
a7b4d8c7ab
Author | SHA1 | Date | |
---|---|---|---|
|
a7b4d8c7ab | ||
|
e633d3875d | ||
|
72d6a678bd | ||
|
3e5e4d5254 | ||
|
c1250bbb7f | ||
|
108d6afbd0 | ||
|
62dd037957 | ||
|
bc621ff9c1 |
8
Cargo.lock
generated
8
Cargo.lock
generated
@ -338,9 +338,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy-inspector-egui"
|
name = "bevy-inspector-egui"
|
||||||
version = "0.28.0"
|
version = "0.28.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dd64580f4496ed987c6231c6a7d833068914331a9084bf5a3dd9dcbc66fd8a73"
|
checksum = "36172627eb6fd8586600972bcbba2880ed6f59e4e243dcf2ed7ff68d987577ce"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy-inspector-egui-derive",
|
"bevy-inspector-egui-derive",
|
||||||
"bevy_app",
|
"bevy_app",
|
||||||
@ -2278,7 +2278,7 @@ checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "forestiles"
|
name = "forestiles"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"android_logger",
|
"android_logger",
|
||||||
"bevy",
|
"bevy",
|
||||||
@ -2402,7 +2402,7 @@ dependencies = [
|
|||||||
"vec_map",
|
"vec_map",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"windows 0.52.0",
|
"windows 0.58.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "forestiles"
|
name = "forestiles"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
@ -30,7 +30,7 @@ bevy = { version = "0.15", default-features = false, features = [
|
|||||||
"bevy_gltf",
|
"bevy_gltf",
|
||||||
"bevy_mesh_picking_backend",
|
"bevy_mesh_picking_backend",
|
||||||
"bevy_pbr",
|
"bevy_pbr",
|
||||||
"bevy_picking",
|
# "bevy_picking",
|
||||||
"bevy_render",
|
"bevy_render",
|
||||||
"bevy_scene",
|
"bevy_scene",
|
||||||
"bevy_sprite",
|
"bevy_sprite",
|
||||||
@ -84,6 +84,9 @@ android_logger = "0.14"
|
|||||||
[profile.dev]
|
[profile.dev]
|
||||||
opt-level = 1
|
opt-level = 1
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
debug = true
|
||||||
|
|
||||||
# Enable a large amount of optimization in the dev profile for dependencies.
|
# Enable a large amount of optimization in the dev profile for dependencies.
|
||||||
[profile.dev.package."*"]
|
[profile.dev.package."*"]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
use bevy::{math::NormedVectorSpace, picking::{focus::HoverMap, pointer::PointerId}, prelude::*, utils::HashMap, window::PrimaryWindow};
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use bevy::{input::mouse::MouseWheel, math::NormedVectorSpace, picking::{focus::HoverMap, pointer::PointerId}, prelude::*, utils::HashMap, window::PrimaryWindow};
|
||||||
|
|
||||||
use crate::ui;
|
use crate::ui;
|
||||||
|
|
||||||
pub struct Plugin;
|
pub struct Plugin;
|
||||||
impl bevy::prelude::Plugin for Plugin {
|
impl bevy::prelude::Plugin for Plugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_systems(Startup, setup)
|
app.add_systems(Startup, setup);
|
||||||
.add_systems(Update, move_cam)
|
// .add_systems(Update, move_cam)
|
||||||
.init_resource::<Pointers>();
|
// .init_resource::<Pointers>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -15,40 +17,39 @@ fn setup(mut cmds: Commands, window: Query<&Window>) {
|
|||||||
let zoom = 2./window.single().width().min(window.single().height());
|
let zoom = 2./window.single().width().min(window.single().height());
|
||||||
cmds.spawn((
|
cmds.spawn((
|
||||||
Camera2d,
|
Camera2d,
|
||||||
Transform::from_scale(Vec3::new(zoom, zoom, 1.))
|
Transform::from_scale(Vec3::new(zoom, zoom, zoom))
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource, Default)]
|
#[derive(Resource, Default)]
|
||||||
struct Pointers(HashMap<PointerId, Vec2>);
|
struct Pointers(HashMap<PointerId, (Vec2, Option<Duration>)>);
|
||||||
|
|
||||||
fn move_cam(
|
fn move_cam(
|
||||||
mut cam: Query<&mut Transform, With<Camera2d>>,
|
mut cam: Query<&mut Transform, With<Camera2d>>,
|
||||||
map_ui_entity: Query<Entity, With<ui::MapUIComponent>>,
|
map_ui_entity: Query<Entity, With<ui::MapUIComponent>>,
|
||||||
mouse_buttons: Res<ButtonInput<MouseButton>>,
|
mouse_buttons: Res<ButtonInput<MouseButton>>,
|
||||||
|
mut ev_scroll: EventReader<MouseWheel>,
|
||||||
touches: Res<Touches>,
|
touches: Res<Touches>,
|
||||||
window: Query<&Window, With<PrimaryWindow>>,
|
window: Query<&Window, With<PrimaryWindow>>,
|
||||||
mut pointers: ResMut<Pointers>,
|
mut pointers: ResMut<Pointers>,
|
||||||
hover_map: Res<HoverMap>
|
hover_map: Res<HoverMap>,
|
||||||
|
time: Res<Time>
|
||||||
) {
|
) {
|
||||||
let window = window.single();
|
let window = window.single();
|
||||||
let mut cam = cam.single_mut();
|
let mut cam = cam.single_mut();
|
||||||
let map_ui_entity = map_ui_entity.single();
|
let map_ui_entity = map_ui_entity.single();
|
||||||
let ps = hover_map.iter().filter_map(|(id, hit_map)| match id {
|
let ps = hover_map.iter().filter_map(|(id, hit_map)| match id {
|
||||||
PointerId::Mouse => window.cursor_position().map(|p| (mouse_buttons.pressed(MouseButton::Left), p, pointers.0.get(id).map(|p| *p).unwrap_or(p))),
|
PointerId::Mouse => window.cursor_position().map(|p|
|
||||||
PointerId::Touch(i) => touches.get_pressed(*i).map(|t| (true, t.position(), t.previous_position())),
|
match pointers.0.get(id) {
|
||||||
|
Some(p_cache) => (p_cache.1.filter(|_| mouse_buttons.pressed(MouseButton::Left)), p, p_cache.0),
|
||||||
|
None => (None, p, p)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
PointerId::Touch(i) => touches.get_pressed(*i).map(|t| (pointers.0.get(id).map(|(pos, start)| (*start).unwrap_or(time.elapsed())), t.position(), t.previous_position())),
|
||||||
_ => None
|
_ => None
|
||||||
}.map(|(pressed,new_pos, old_pos)| (pressed,new_pos,old_pos,id,hit_map))
|
}.map(|(pressed_start,new_pos, old_pos)| (pressed_start,new_pos,old_pos,id,hit_map))
|
||||||
).collect::<Vec<_>>();
|
).collect::<Vec<_>>();
|
||||||
let pressed_on_map = ps.iter().filter(|p| p.0 && p.4.contains_key(&map_ui_entity)).collect::<Vec<_>>();
|
let pressed_on_map = ps.iter().filter(|p| p.0.is_some() && p.4.contains_key(&map_ui_entity)).collect::<Vec<_>>();
|
||||||
// let mut movement = Vec2::ZERO;
|
|
||||||
// for (pressed, new_pos, old_pos, id, hit_map) in pressed_on_map.iter() {
|
|
||||||
// let delta = new_pos - old_pos;
|
|
||||||
// cam.scale += delta.dot(movement)/delta.norm();
|
|
||||||
// movement.x -= delta.x*cam.scale.x/pressed_num as f32;
|
|
||||||
// movement.y += delta.y*cam.scale.y/pressed_num as f32;
|
|
||||||
// pointers.0.insert(**id, *new_pos);
|
|
||||||
// }
|
|
||||||
|
|
||||||
let old_midpoint = pressed_on_map.iter().fold(Vec2::ZERO, |acc, (_, _, old_pos, _, _)| {
|
let old_midpoint = pressed_on_map.iter().fold(Vec2::ZERO, |acc, (_, _, old_pos, _, _)| {
|
||||||
acc + (old_pos/pressed_on_map.len() as f32)
|
acc + (old_pos/pressed_on_map.len() as f32)
|
||||||
@ -56,9 +57,12 @@ fn move_cam(
|
|||||||
let new_midpoint = pressed_on_map.iter().fold(Vec2::ZERO, |acc, (_, new_pos, _, _, _)| {
|
let new_midpoint = pressed_on_map.iter().fold(Vec2::ZERO, |acc, (_, new_pos, _, _, _)| {
|
||||||
acc + (new_pos/pressed_on_map.len() as f32)
|
acc + (new_pos/pressed_on_map.len() as f32)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// move camera
|
||||||
cam.translation.x -= (new_midpoint.x - old_midpoint.x)*cam.scale.x;
|
cam.translation.x -= (new_midpoint.x - old_midpoint.x)*cam.scale.x;
|
||||||
cam.translation.y += (new_midpoint.y - old_midpoint.y)*cam.scale.y;
|
cam.translation.y += (new_midpoint.y - old_midpoint.y)*cam.scale.y;
|
||||||
|
|
||||||
|
// multiple fingers zoom
|
||||||
if pressed_on_map.len() > 1 {
|
if pressed_on_map.len() > 1 {
|
||||||
let old_d_to_midpoint = pressed_on_map.iter().fold(0., |acc, (_, _, old_pos, _, _)| {
|
let old_d_to_midpoint = pressed_on_map.iter().fold(0., |acc, (_, _, old_pos, _, _)| {
|
||||||
acc + (old_midpoint-old_pos).norm()
|
acc + (old_midpoint-old_pos).norm()
|
||||||
@ -67,12 +71,22 @@ fn move_cam(
|
|||||||
acc + (new_midpoint-new_pos).norm()
|
acc + (new_midpoint-new_pos).norm()
|
||||||
});
|
});
|
||||||
let zoom = new_d_to_midpoint/old_d_to_midpoint;
|
let zoom = new_d_to_midpoint/old_d_to_midpoint;
|
||||||
dbg!(zoom);
|
cam.scale /= zoom;
|
||||||
cam.scale.x /= zoom;
|
|
||||||
cam.scale.y /= zoom;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (_, new_pos, _, id, _) in ps {
|
// mouse scroll zoom
|
||||||
pointers.0.insert(*id, new_pos);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update cached pointer positions
|
||||||
|
pointers.0.clear();
|
||||||
|
for (pressed_start, new_pos, _, id, _) in ps {
|
||||||
|
match id {
|
||||||
|
PointerId::Mouse => {pointers.0.insert(*id, (new_pos, pressed_start));},
|
||||||
|
PointerId::Touch(_) => {pointers.0.insert(*id, (new_pos, pressed_start));},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
108
src/map.rs
108
src/map.rs
@ -1,9 +1,13 @@
|
|||||||
use bevy::{asset::RenderAssetUsages, prelude::*, render::mesh::{Indices, PrimitiveTopology}};
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use bevy::{asset::RenderAssetUsages, picking::PickSet, prelude::*, render::mesh::{Indices, PrimitiveTopology}, utils::HashMap};
|
||||||
use noise::{Fbm, MultiFractal, NoiseFn, Perlin};
|
use noise::{Fbm, MultiFractal, NoiseFn, Perlin};
|
||||||
use rand::{Rng, SeedableRng};
|
use rand::{thread_rng, Rng, SeedableRng};
|
||||||
use voronoice::{BoundingBox, Point, VoronoiBuilder};
|
use voronoice::{BoundingBox, Point, VoronoiBuilder};
|
||||||
|
|
||||||
mod cells;
|
mod cells;
|
||||||
|
mod picking;
|
||||||
|
use picking::*;
|
||||||
use cells::*;
|
use cells::*;
|
||||||
|
|
||||||
pub struct Plugin;
|
pub struct Plugin;
|
||||||
@ -12,8 +16,10 @@ impl bevy::prelude::Plugin for Plugin {
|
|||||||
app.add_systems(Startup, setup)
|
app.add_systems(Startup, setup)
|
||||||
.insert_resource(Time::<Fixed>::from_seconds(0.25)) // Time for a day
|
.insert_resource(Time::<Fixed>::from_seconds(0.25)) // Time for a day
|
||||||
.add_systems(FixedUpdate, update_cells)
|
.add_systems(FixedUpdate, update_cells)
|
||||||
|
.add_systems(PreUpdate, picking_backend.in_set(PickSet::Backend))
|
||||||
|
.add_systems(Update, update_map_mesh)
|
||||||
.insert_resource(ClearColor(Color::srgb(0., 0., 1.)))
|
.insert_resource(ClearColor(Color::srgb(0., 0., 1.)))
|
||||||
.insert_resource(Seed(0));
|
.insert_resource(Seed(thread_rng().gen()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,7 +34,7 @@ pub const SIZE: usize = 10000;
|
|||||||
struct Seed(u32);
|
struct Seed(u32);
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
struct Voronoi (voronoice::Voronoi);
|
pub struct Voronoi (voronoice::Voronoi);
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct MapMarker;
|
pub struct MapMarker;
|
||||||
@ -36,6 +42,12 @@ pub struct MapMarker;
|
|||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
struct MapColors (Vec<[f32; 4]>);
|
struct MapColors (Vec<[f32; 4]>);
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct CellsEntities (Vec<Entity>);
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct MeshNeedsUpdate(bool);
|
||||||
|
|
||||||
fn setup(
|
fn setup(
|
||||||
mut cmds: Commands,
|
mut cmds: Commands,
|
||||||
mut meshes: ResMut<Assets<Mesh>>,
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
@ -73,7 +85,7 @@ fn setup(
|
|||||||
} else if z <= 0.52 {
|
} else if z <= 0.52 {
|
||||||
CellKind::Beach
|
CellKind::Beach
|
||||||
} else if z < 0.8 {
|
} else if z < 0.8 {
|
||||||
CellKind::Grass
|
CellKind::Dirt
|
||||||
} else {
|
} else {
|
||||||
CellKind::Stone
|
CellKind::Stone
|
||||||
};
|
};
|
||||||
@ -118,21 +130,36 @@ fn setup(
|
|||||||
)
|
)
|
||||||
.with_inserted_indices(Indices::U32(indices));
|
.with_inserted_indices(Indices::U32(indices));
|
||||||
|
|
||||||
|
let mut cells_entities = Vec::with_capacity(cells_data.len());
|
||||||
cmds.spawn((
|
cmds.spawn((
|
||||||
Mesh2d(meshes.add(mesh)),
|
Mesh2d(meshes.add(mesh)),
|
||||||
MeshMaterial2d(materials.add(ColorMaterial::default())),
|
MeshMaterial2d(materials.add(ColorMaterial::default())),
|
||||||
Transform::default(),
|
Transform::default(),
|
||||||
Voronoi(voronoi),
|
Voronoi(voronoi),
|
||||||
MapColors(colors),
|
MapColors(colors),
|
||||||
|
MeshNeedsUpdate(true),
|
||||||
MapMarker
|
MapMarker
|
||||||
)).with_children(|parent| {
|
)).with_children(|parent| {
|
||||||
for cd in cells_data {
|
for cd in cells_data {
|
||||||
match cd.kind {
|
let mut cmd = parent.spawn((cd, LastUpdate(0)));
|
||||||
CellKind::Forest | CellKind::Grass => parent.spawn((cd, LastUpdate(0))),
|
cmd.observe(|trigger: Trigger<Pointer<Click>>, mut cells: Query<&mut CellData>, mut map_needs_update: Query<&mut MeshNeedsUpdate, With<MapMarker>>| {
|
||||||
_ => parent.spawn(cd)
|
if trigger.duration > Duration::from_millis(100) {
|
||||||
};
|
return
|
||||||
|
}
|
||||||
|
let mut cd = cells.get_mut(trigger.target).unwrap();
|
||||||
|
match cd.kind {
|
||||||
|
CellKind::Dirt | CellKind::Grass => {
|
||||||
|
cd.kind = CellKind::Forest;
|
||||||
|
map_needs_update.single_mut().0 = true;
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
dbg!(trigger.duration);
|
||||||
|
});
|
||||||
|
cells_entities.push(cmd.id());
|
||||||
}
|
}
|
||||||
});
|
}).insert(CellsEntities(cells_entities));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
@ -140,32 +167,43 @@ pub struct LastUpdate(usize);
|
|||||||
|
|
||||||
fn update_cells(
|
fn update_cells(
|
||||||
mut cells: Query<(&mut CellData, &mut LastUpdate)>,
|
mut cells: Query<(&mut CellData, &mut LastUpdate)>,
|
||||||
mut map: Query<(&Mesh2d, &mut MapColors), With<MapMarker>>,
|
mut map_needs_update: Query<&mut MeshNeedsUpdate, With<MapMarker>>
|
||||||
mut meshes: ResMut<Assets<Mesh>>
|
|
||||||
) {
|
) {
|
||||||
let (mesh, mut cols) = map.single_mut();
|
let mut map_needs_update = map_needs_update.single_mut();
|
||||||
if let Some(mesh) = meshes.get_mut(mesh) {
|
for (mut cd, mut lu) in cells.iter_mut() {
|
||||||
// let cols = mesh.attribute_mut(Mesh::ATTRIBUTE_COLOR).unwrap();
|
lu.0 += 1;
|
||||||
let mut modified = false;
|
if lu.0 > match cd.kind {
|
||||||
for (mut cd, mut lu) in cells.iter_mut() {
|
CellKind::Void | CellKind::Sea | CellKind::Beach | CellKind::Dirt | CellKind::Stone => usize::MAX,
|
||||||
lu.0 += 1;
|
CellKind::Forest => 100*365/4, // Let's say that a forest takes 100 years to mature
|
||||||
if lu.0 > match cd.kind {
|
CellKind::Grass => 7*7/4 // Let's say that grass takes 7 weaks to reach its max
|
||||||
CellKind::Void | CellKind::Sea | CellKind::Beach | CellKind::Dirt | CellKind::Stone => usize::MAX,
|
} {
|
||||||
CellKind::Forest => 100*365/4, // Let's say that a forest takes 100 years to mature
|
lu.0 = 0;
|
||||||
CellKind::Grass => 7*7/4 // Let's say that grass takes 7 weaks to reach its max
|
cd.resource = (cd.resource + 1).clamp(0, 4);
|
||||||
} {
|
map_needs_update.0 = true;
|
||||||
lu.0 = 0;
|
|
||||||
cd.resource = (cd.resource + 1).clamp(0, 4);
|
|
||||||
modified = true;
|
|
||||||
}
|
|
||||||
// cd.update();
|
|
||||||
let col = cd.color();
|
|
||||||
for id in cd.vertices.iter() {
|
|
||||||
cols.0[*id] = col.clone();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if modified {
|
|
||||||
mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, cols.0.clone());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_map_mesh(
|
||||||
|
cells: Query<&CellData>,
|
||||||
|
mut map: Query<(&Mesh2d, &mut MapColors, &mut MeshNeedsUpdate), With<MapMarker>>,
|
||||||
|
mut meshes: ResMut<Assets<Mesh>>
|
||||||
|
) {
|
||||||
|
let (mesh, mut cols, mut needs_update) = map.single_mut();
|
||||||
|
if needs_update.0 {
|
||||||
|
if let Some(mesh) = meshes.get_mut(mesh) {
|
||||||
|
let mut modified = false;
|
||||||
|
for cd in cells.iter() {
|
||||||
|
let col = cd.color();
|
||||||
|
for id in cd.vertices.iter() {
|
||||||
|
modified = modified || cols.0[*id] != col;
|
||||||
|
cols.0[*id] = col.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if modified {
|
||||||
|
mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, cols.0.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
needs_update.0 = false;
|
||||||
|
}
|
||||||
}
|
}
|
39
src/map/picking.rs
Normal file
39
src/map/picking.rs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
use bevy::{picking::{backend::{HitData, PointerHits}, pointer::{PointerId, PointerLocation}}, prelude::*, window::PrimaryWindow};
|
||||||
|
use voronoice::Point;
|
||||||
|
|
||||||
|
use super::{CellsEntities, MapMarker, Voronoi};
|
||||||
|
|
||||||
|
pub fn picking_backend(
|
||||||
|
cam: Query<(&Transform, Entity), With<Camera2d>>,
|
||||||
|
window: Query<&Window, With<PrimaryWindow>>,
|
||||||
|
pointers: Query<(&PointerId, &PointerLocation)>,
|
||||||
|
map: Query<(&Voronoi, &CellsEntities, &Transform), With<MapMarker>>,
|
||||||
|
mut output: EventWriter<PointerHits>
|
||||||
|
) {
|
||||||
|
let (cam, cam_id) = cam.single();
|
||||||
|
let window = window.single();
|
||||||
|
let (voronoi, cells_entities, map_pos) = map.single();
|
||||||
|
let mut last_cell = 0;
|
||||||
|
for (id, l) in pointers.iter() {
|
||||||
|
if let Some(mut pos) = l.location().map(|l| l.position) {
|
||||||
|
pos -= window.size()/2.;
|
||||||
|
pos *= cam.scale.xy();
|
||||||
|
pos.x += cam.translation.x;
|
||||||
|
pos.y -= cam.translation.y;
|
||||||
|
if let Some(c) = voronoi.0.cell(last_cell).iter_path(Point { x: pos.x as f64, y: -pos.y as f64 }).last() {
|
||||||
|
last_cell = c;
|
||||||
|
output.send(PointerHits {
|
||||||
|
pointer: *id,
|
||||||
|
picks: vec![(cells_entities.0[c], HitData {
|
||||||
|
camera: cam_id,
|
||||||
|
depth: map_pos.translation.z,
|
||||||
|
position: Some(Vec3 { x: pos.x, y: pos.y, z: map_pos.translation.z }),
|
||||||
|
normal: None
|
||||||
|
})],
|
||||||
|
order: map_pos.translation.z
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
77
src/ui.rs
77
src/ui.rs
@ -1,12 +1,20 @@
|
|||||||
use bevy::prelude::*;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use bevy::{input::mouse::MouseWheel, math::{NormedVectorSpace, VectorSpace}, picking::{focus::HoverMap, pointer::PointerId}, prelude::*, utils::HashMap, window::PrimaryWindow};
|
||||||
|
|
||||||
|
use crate::map::{self, MapMarker};
|
||||||
|
|
||||||
pub struct Plugin;
|
pub struct Plugin;
|
||||||
impl bevy::prelude::Plugin for Plugin {
|
impl bevy::prelude::Plugin for Plugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_systems(Startup, setup);
|
app.add_systems(Startup, setup)
|
||||||
|
.add_systems(Update, zoom_with_scroll);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
struct PointersDragging(HashMap<PointerId, Vec2>);
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct MapUIComponent;
|
pub struct MapUIComponent;
|
||||||
|
|
||||||
@ -21,7 +29,68 @@ fn setup(
|
|||||||
justify_content: JustifyContent::Center,
|
justify_content: JustifyContent::Center,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
MapUIComponent
|
PickingBehavior {
|
||||||
));
|
should_block_lower: false,
|
||||||
|
is_hoverable: true
|
||||||
|
},
|
||||||
|
MapUIComponent,
|
||||||
|
PointersDragging(HashMap::new())
|
||||||
|
)).observe(|trigger: Trigger<Pointer<DragStart>>, mut ptrs: Query<&mut PointersDragging>| {
|
||||||
|
let event = trigger.event();
|
||||||
|
// dbg!(event);
|
||||||
|
if event.button == PointerButton::Primary {
|
||||||
|
ptrs.get_mut(event.target).unwrap().0.insert(event.pointer_id, event.pointer_location.position);
|
||||||
|
}
|
||||||
|
}).observe(|trigger: Trigger<Pointer<DragEnd>>, mut ptrs: Query<&mut PointersDragging>| {
|
||||||
|
let event = trigger.event();
|
||||||
|
// dbg!(event);
|
||||||
|
if event.button == PointerButton::Primary {
|
||||||
|
ptrs.get_mut(event.target).unwrap().0.remove(&event.pointer_id);
|
||||||
|
}
|
||||||
|
}).observe(|
|
||||||
|
trigger: Trigger<Pointer<Drag>>,
|
||||||
|
mut ptrs: Query<&mut PointersDragging>,
|
||||||
|
mut cam: Query<&mut Transform, With<Camera2d>>,
|
||||||
|
| {
|
||||||
|
let event = trigger.event();
|
||||||
|
// dbg!(event);
|
||||||
|
if event.button == PointerButton::Primary {
|
||||||
|
let mut cam = cam.single_mut();
|
||||||
|
let mut ptrs = ptrs.get_mut(event.target).unwrap();
|
||||||
|
|
||||||
|
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(event.pointer_id, event.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
// Spawn all ui elements as children of this one
|
// Spawn all ui elements as children of this one
|
||||||
|
}
|
||||||
|
|
||||||
|
fn zoom_with_scroll(
|
||||||
|
mut cam: Query<&mut Transform, With<Camera2d>>,
|
||||||
|
mut ev_scroll: EventReader<MouseWheel>,
|
||||||
|
hover_map: Res<HoverMap>,
|
||||||
|
window: Query<&Window, With<PrimaryWindow>>,
|
||||||
|
map_ui_id: Query<Entity, With<MapUIComponent>>
|
||||||
|
) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user