Newer
Older
use bevy::input::mouse::{MouseMotion, MouseWheel};
use bevy::math::{vec3, Vec2, Vec3Swizzles};
Camera2dBundle, Commands, Component, CoreStage, Entity, EventReader, Input, Local, MouseButton,
OrthographicProjection, Plugin, Projection, Query, Res, Transform, Windows, With, Without,
use iyes_loopless::prelude::{AppLooplessStateExt, ConditionSet};
use crate::system::flow::AppState;
use crate::system::load_config::virtual_size;
use crate::system::utilities::{f32_max, f32_min};
/// A flag component to indicate which entity should be followed by the camera
#[derive(Component)]
pub struct ChaseCam;
/// A flag component to indicate a camera that should be used for rendering world entities and sprites
#[derive(Component)]
pub struct GameCamera;
/// System that creates a default orthographic camera, with correct tags for querying
pub fn spawn_orthographic_camera(mut commands: Commands) {
let (target_width, target_height) = virtual_size();
commands.spawn((
Camera2dBundle {
projection: OrthographicProjection {
left: -(target_width / 2.0),
right: (target_width / 2.0),
top: (target_height / 2.0),
bottom: -(target_height / 2.0),
scaling_mode: ScalingMode::Auto {
min_width: target_width,
min_height: target_height,
},
..Default::default()
},
..Default::default()
},
GameCamera,
));
}
pub fn pan_camera(
mut ev_motion: EventReader<MouseMotion>,
mut ev_scroll: EventReader<MouseWheel>,
mut query: Query<(&mut Transform, &OrthographicProjection), With<GameCamera>>,
mut last_mouse_pos: Local<Vec2>,
if let Some(mouse) = windows
.get_primary()
.and_then(|window| window.cursor_position())
{
let previous_mouse = std::mem::replace(&mut *last_mouse_pos, mouse);
let mouse_diff = mouse - previous_mouse;
if input_mouse.pressed(MouseButton::Right) {
for ev in ev_motion.iter() {
pan += ev.delta;
}
if mouse_diff != Vec2::ZERO && mouse_diff.x < 50.0 && mouse_diff.y < 50.0 {
for (mut transform, projection) in &mut query {
transform.translation -= mouse_diff.extend(0.0);
}
}
}
/// System that takes the average location of all chase camera entities, and updates the location
/// of all world cameras to track the average location.
///
/// e.g. If a player entity is chased, and a mouse tracking entity is chased, the world cameras will
/// by updated to the midpoint between the player and the mouse
pub fn sync_chase_camera_location(
mut commands: Commands,
chased_query: Query<&Transform, (With<ChaseCam>, Without<GameCamera>)>,
mut camera_query: Query<
(Entity, &mut Transform, &OrthographicProjection),
(With<GameCamera>, Without<ChaseCam>),
>,
map_query: MapQuery,
let mut average_location = Vec2::new(0.0, 0.0);
let mut count = 0;
for location in chased_query.iter() {
average_location += location.translation.xy();
count += 1;
}
if count > 0 {
average_location /= count as f32;
}
for (_, mut location, proj) in camera_query.iter_mut() {
if let Some(bounds) = map_query.get_camera_bounds() {
// log::info!("BOUNDS {:?}", bounds);
let width = proj.right - proj.left;
let height = proj.top - proj.bottom;
let val_x = f32_max(
bounds.get_min_x(width),
f32_min(bounds.get_max_x(width), average_location.x),
);
let val_y = f32_max(
bounds.get_min_y(height),
f32_min(bounds.get_max_y(height), average_location.y),
);
location.translation = vec3(val_x, val_y, location.translation.z);
} else {
location.translation = average_location.extend(location.translation.z);
}
}
}
/// A marker struct for spawning and managing cameras. Cameras will be created on startup, and
/// will constantly have their positions synced
pub struct CameraManagementPlugin;
impl Plugin for CameraManagementPlugin {
fn build(&self, app: &mut App) {
app.add_enter_system(AppState::Preload, spawn_orthographic_camera)
.add_system_to_stage(CoreStage::PreUpdate, sync_chase_camera_location)
.add_system_set(
ConditionSet::new()
.run_in_state(AppState::InGame)
.with_system(pan_camera)
.into(),
);