use bevy::ecs::query::ReadOnlyWorldQuery; use bevy::ecs::system::SystemParam; use bevy::prelude::*; use crate::MapQuery; pub fn lock_camera_to_level<CameraSelector: ReadOnlyWorldQuery>( map_query: MapQuery, mut camera_query: Query<(&mut Transform, &OrthographicProjection), CameraSelector>, ) { let bounds = match map_query.get_camera_bounds() { Some(bounds) => bounds, None => return, }; for (mut transform, proj) in &mut camera_query { let rect = proj.area; let width = rect.width(); let height = rect.height(); let val_x = bounds .get_min_x(width) .max(bounds.get_max_x(width).min(transform.translation.x)); let val_y = bounds .get_min_y(height) .max(bounds.get_max_y(height).min(transform.translation.y)); transform.translation = Vec3::new(val_x, val_y, transform.translation.z); } } #[derive(SystemParam)] pub struct CameraBounder<'w, 's, Filter: ReadOnlyWorldQuery + 'static> { map_query: MapQuery<'w, 's>, query: Query<'w, 's, &'static OrthographicProjection, Filter>, } impl<'w, 's, Filter: ReadOnlyWorldQuery + 'static> CameraBounder<'w, 's, Filter> { pub fn get_extremeties(&self) -> Option<Rect> { if let Some(bounds) = self.map_query.get_camera_bounds() { if let Ok(proj) = self.query.get_single() { let rect = proj.area; let width = rect.width(); let height = rect.height(); Some(Rect::new( bounds.get_min_x(width), bounds.get_min_y(height), bounds.get_max_x(width), bounds.get_max_y(height), )) } else { None } } else { None } } pub fn bind(&self, point: Vec2) -> Vec2 { if let Some(bounds) = self.get_extremeties() { bounds.min.max(point).min(bounds.max) } else { point } } } #[cfg(test)] mod test { use bevy::math::{Rect, Vec2}; #[test] fn min_max_vec() { let initial_point = Vec2::new(3.0, 5.0); let expected_point = Vec2::new(3.0, 3.0); let bounds = Rect::new(1.0, 1.0, 3.0, 3.0); assert_eq!( expected_point, bounds.min.max(initial_point).min(bounds.max) ); } }