📄 src/camera_controller.rs
use bevy::{
    app::{App, Plugin, Update},
    camera::Camera,
    ecs::{
        query::With,
        resource::Resource,
        schedule::IntoScheduleConfigs,
        system::{If, Res, Single},
    },
    input::{
        ButtonInput,
        gamepad::{Gamepad, GamepadAxis, GamepadButton},
        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, move_camera_with_controller).chain(),
        );
    }
}

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

fn move_camera_with_controller(
    settings: Res<CameraSettings>,
    mut camera: Single<&mut Transform, With<Camera>>,
    gamepad: If<Single<&Gamepad>>,
) {
    let mut origin = settings.origin;

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

    origin += camera.local_z().as_vec3()
        * (gamepad
            .get(GamepadButton::LeftTrigger2)
            .and_then(|r| gamepad.get(GamepadButton::RightTrigger2).map(|l| r - l)))
        .unwrap_or_default();
    origin += camera.local_x().as_vec3() * gamepad.get(GamepadAxis::LeftStickX).unwrap_or_default();
    origin += camera.local_y().as_vec3() * gamepad.get(GamepadAxis::LeftStickY).unwrap_or_default();

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

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);
}