use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
use bevy_input::mouse::AccumulatedMouseMotion;
use bevy_input::prelude::*;
use bevy_math::Vec3;
use bevy_reflect::Reflect;
use bevy_time::Time;
use bevy_transform::components::Transform;
use bevy_window::{CursorGrabMode, CursorOptions, PrimaryWindow, Window};
#[derive(Component, Debug, Clone, Copy, Reflect)]
#[reflect(Component)]
pub struct FlyCam {
pub speed: f32,
pub boost: f32,
pub sensitivity: f32,
pub mouse_look: bool,
}
impl Default for FlyCam {
fn default() -> Self {
Self {
speed: 60.0,
boost: 4.0,
sensitivity: 0.003,
mouse_look: true,
}
}
}
pub struct CameraPlugin;
impl Plugin for CameraPlugin {
fn build(&self, app: &mut App) {
app.register_type::<FlyCam>();
app.add_systems(Update, (fly_camera, cursor_grab));
}
}
pub fn fly_camera(
time: Res<Time>,
keyboard: Res<ButtonInput<KeyCode>>,
mouse: Res<AccumulatedMouseMotion>,
cursor_q: Query<&CursorOptions, With<PrimaryWindow>>,
mut q: Query<(&mut Transform, &FlyCam)>,
) {
let dt = time.delta_secs();
let cursor_locked = cursor_q
.single()
.map(|c| matches!(c.grab_mode, CursorGrabMode::Locked))
.unwrap_or(false);
for (mut transform, cam) in q.iter_mut() {
let forward = Vec3::from(transform.forward());
let right = Vec3::from(transform.right());
let mut velocity = Vec3::ZERO;
if keyboard.pressed(KeyCode::KeyW) {
velocity += forward;
}
if keyboard.pressed(KeyCode::KeyS) {
velocity -= forward;
}
if keyboard.pressed(KeyCode::KeyA) {
velocity -= right;
}
if keyboard.pressed(KeyCode::KeyD) {
velocity += right;
}
if keyboard.pressed(KeyCode::KeyE) {
velocity.y += 1.0;
}
if keyboard.pressed(KeyCode::KeyQ) {
velocity.y -= 1.0;
}
if velocity != Vec3::ZERO {
velocity = velocity.normalize();
}
let boost = if keyboard.pressed(KeyCode::ShiftLeft)
|| keyboard.pressed(KeyCode::ShiftRight)
{
cam.boost
} else {
1.0
};
transform.translation += velocity * cam.speed * boost * dt;
if cam.mouse_look && cursor_locked && mouse.delta != bevy_math::Vec2::ZERO {
let yaw = -mouse.delta.x * cam.sensitivity;
let pitch = -mouse.delta.y * cam.sensitivity;
transform.rotate_y(yaw);
transform.rotate_local_x(pitch);
}
}
}
pub fn cursor_grab(
mut cursor_q: Query<&mut CursorOptions, With<PrimaryWindow>>,
mouse: Res<ButtonInput<MouseButton>>,
keys: Res<ButtonInput<KeyCode>>,
) {
let Ok(mut cursor) = cursor_q.single_mut() else {
return;
};
if mouse.just_pressed(MouseButton::Left) {
cursor.grab_mode = CursorGrabMode::Locked;
cursor.visible = false;
}
if keys.just_pressed(KeyCode::Escape) {
cursor.grab_mode = CursorGrabMode::None;
cursor.visible = true;
}
}
#[allow(dead_code)]
type _KeepWindow = Window;