1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use bevy::{input::mouse::MouseMotion, prelude::*};

pub(crate) struct FlycamPlugin;
impl Plugin for FlycamPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(
            Update,
            camera_movement.in_set(CameraSystem::EditorCam3dFree),
        )
        .add_systems(Update, camera_look);
    }
}

#[derive(SystemSet, PartialEq, Eq, Clone, Hash, Debug)]
pub(crate) enum CameraSystem {
    EditorCam3dFree,
}

#[derive(Component)]
pub struct FlycamControls {
    pub yaw: f32,
    pub pitch: f32,
    pub sensitivity: f32,
    pub enable_movement: bool,
    pub enable_look: bool,

    pub key_forward: KeyCode,
    pub key_back: KeyCode,
    pub key_left: KeyCode,
    pub key_right: KeyCode,
    pub key_up: KeyCode,
    pub key_down: KeyCode,
    pub key_boost: KeyCode,
}
impl Default for FlycamControls {
    fn default() -> Self {
        Self {
            yaw: Default::default(),
            pitch: Default::default(),
            sensitivity: 1.0,
            enable_movement: false,
            enable_look: false,
            key_forward: KeyCode::KeyW,
            key_back: KeyCode::KeyS,
            key_left: KeyCode::KeyA,
            key_right: KeyCode::KeyD,
            key_up: KeyCode::Space,
            key_down: KeyCode::ControlLeft,
            key_boost: KeyCode::ShiftLeft,
        }
    }
}

fn camera_movement(
    mut cam: Query<(&FlycamControls, &mut Transform)>,
    time: Res<Time>,
    keyboard_input: Res<ButtonInput<KeyCode>>,
) {
    let (flycam, mut cam_transform) = cam.single_mut();
    if !flycam.enable_movement {
        return;
    }

    let if_then_1 = |b| if b { 1.0 } else { 0.0 };
    let forward = if_then_1(keyboard_input.pressed(flycam.key_forward))
        - if_then_1(keyboard_input.pressed(flycam.key_back));
    let sideways = if_then_1(keyboard_input.pressed(flycam.key_right))
        - if_then_1(keyboard_input.pressed(flycam.key_left));
    let up = if_then_1(keyboard_input.pressed(flycam.key_up))
        - if_then_1(keyboard_input.pressed(flycam.key_down));

    if forward == 0.0 && sideways == 0.0 && up == 0.0 {
        return;
    }

    let speed = if keyboard_input.pressed(flycam.key_boost) {
        20.0
    } else {
        5.0
    };

    let movement =
        Vec3::new(sideways, forward, up).normalize_or_zero() * speed * time.delta_seconds();

    let diff = cam_transform.forward() * movement.y
        + cam_transform.right() * movement.x
        + cam_transform.up() * movement.z;
    cam_transform.translation += diff;
}

fn camera_look(
    mouse_input: Res<ButtonInput<MouseButton>>,
    mut mouse_motion_event_reader: EventReader<MouseMotion>,
    mut query: Query<(&mut FlycamControls, &mut Transform)>,
) {
    let (mut flycam, mut transform) = query.single_mut();
    if !flycam.enable_look || !mouse_input.pressed(MouseButton::Right) {
        //Prevent accumulation of irrelevant events
        mouse_motion_event_reader.clear();
        return;
    }
    let mut delta: Vec2 = Vec2::ZERO;
    for event in mouse_motion_event_reader.read() {
        delta += event.delta;
    }
    if delta.is_nan() || delta.abs_diff_eq(Vec2::ZERO, f32::EPSILON) {
        return;
    }

    flycam.yaw -= delta.x / 180.0 * flycam.sensitivity;
    flycam.pitch -= delta.y / 180.0 * flycam.sensitivity;

    flycam.pitch = flycam
        .pitch
        .clamp(-std::f32::consts::PI / 2.0, std::f32::consts::PI / 2.0);

    transform.rotation = Quat::from_euler(EulerRot::YXZ, flycam.yaw, flycam.pitch, 0.0);
}