ui/
camera.rs

1//! Contains data structures and configuration related to the UI camera(s).
2
3use bevy::input::mouse::{AccumulatedMouseMotion, AccumulatedMouseScroll};
4use bevy::prelude::Projection::Orthographic;
5use bevy::prelude::{
6    Bundle, ButtonInput, Camera2d, Commands, Component, Local, MouseButton, Name, Projection, Res,
7    Single, Transform, With,
8};
9use bevy_egui::PrimaryEguiContext;
10
11/// Marker component for the UI's camera bundle.
12#[derive(Default, Component)]
13pub struct UICamera;
14
15/// Maintains the UI camera state (moving, zooming, ...).
16#[derive(Default)]
17pub(crate) struct UICameraState {
18    /// Whether the camera is following mouse movement.
19    pub moving: bool,
20}
21
22impl UICamera {
23    /// Generates a `Bundle` with a [`UICamera`].
24    #[must_use]
25    pub fn bundle() -> impl Bundle {
26        (Name::new("UI Camera"), Self, PrimaryEguiContext, Camera2d)
27    }
28}
29
30/// Spawns the [`UICamera`] bundle.
31#[utils::bevy_system]
32pub(crate) fn setup_ui_camera(mut commands: Commands) {
33    commands.spawn(UICamera::bundle());
34}
35
36/// The system that controls the UI camera.
37///
38/// It handles moving, zooming and other functionality in response to user input.
39#[utils::bevy_system]
40pub(crate) fn camera_control_system(
41    mut state: Local<UICameraState>,
42    mouse_input: Res<ButtonInput<MouseButton>>,
43    mut camera: Single<(&mut Transform, &mut Projection), With<UICamera>>,
44    mouse_motion: Res<AccumulatedMouseMotion>,
45    mouse_scroll: Res<AccumulatedMouseScroll>,
46) {
47    if mouse_input.just_pressed(MouseButton::Right) {
48        state.moving = true;
49    } else if mouse_input.just_released(MouseButton::Right) {
50        state.moving = false;
51    }
52
53    let (transform, projection) = &mut *camera;
54
55    if state.moving {
56        transform.translation.x -= mouse_motion.delta.x;
57        transform.translation.y += mouse_motion.delta.y;
58    }
59
60    if let Orthographic(projection) = &mut **projection {
61        projection.scale /= 1.0 + mouse_scroll.delta.y * 0.01;
62    }
63}