📄 src/camera_controller.rs
use bevy::{
    app::{App, Plugin, Update},
    camera::Camera,
    ecs::{
        query::With,
        resource::Resource,
        system::{Res, Single},
    },
    input::{ButtonInput, keyboard::KeyCode},
    math::Vec3,
    transform::components::Transform,
};

pub struct CameraControllerPlugin {
    origin: Vec3,
    target: Vec3,
}

impl CameraControllerPlugin {
    pub fn new(origin: Vec3, target: Vec3) -> Self {
        Self { origin, target }
    }
}

impl Plugin for CameraControllerPlugin {
    fn build(&self, app: &mut App) {
        app.insert_resource(CameraSettings {
            origin: self.origin,
            target: self.target,
        })
        .add_systems(Update, move_camera_with_keyboard);
    }
}

#[derive(Resource)]
struct CameraSettings {
    origin: Vec3,
    target: Vec3,
}

fn move_camera_with_keyboard(
    settings: Res<CameraSettings>,
    mut camera: Single<&mut Transform, With<Camera>>,
    input: Res<ButtonInput<KeyCode>>,
) {
    let mut origin = settings.origin;

    camera.translation = origin;
    camera.look_at(settings.target, Vec3::Y);

    if input.pressed(KeyCode::KeyS) {
        origin += camera.local_z().as_vec3();
    }
    if input.pressed(KeyCode::KeyW) {
        origin -= camera.local_z().as_vec3();
    }
    if input.pressed(KeyCode::KeyD) {
        origin += camera.local_x().as_vec3();
    }
    if input.pressed(KeyCode::KeyA) {
        origin -= camera.local_x().as_vec3();
    }
    if input.pressed(KeyCode::KeyE) {
        origin += camera.local_y().as_vec3();
    }
    if input.pressed(KeyCode::KeyQ) {
        origin -= camera.local_y().as_vec3();
    }

    camera.translation = origin;
    camera.look_at(settings.target, Vec3::Y);
}