| @@ -5,6 +5,7 @@ use bevy::{ |
| 5 |
5 |
}; |
| 6 |
6 |
|
| 7 |
7 |
use crate::{ |
|
8 |
+ actions::Action, |
| 8 |
9 |
world::WorldMap, |
| 9 |
10 |
worldgen::{WorldGenerated, spawn_tiles}, |
| 10 |
11 |
}; |
| @@ -22,6 +23,7 @@ impl Plugin for CameraPlugin { |
| 22 |
23 |
keyboard_pan, |
| 23 |
24 |
drag_pan, |
| 24 |
25 |
scroll_zoom, |
|
26 |
+ button_zoom, |
| 25 |
27 |
clamp_to_world, |
| 26 |
28 |
) |
| 27 |
29 |
.chain(), |
| @@ -50,6 +52,30 @@ impl Default for CameraController { |
| 50 |
52 |
} |
| 51 |
53 |
} |
| 52 |
54 |
|
|
55 |
+const BUTTON_ZOOM_LINES: f32 = 2.0; |
|
56 |
+ |
|
57 |
+impl CameraController { |
|
58 |
+ pub fn zoomed(&self, scale: f32, lines: f32) -> f32 { |
|
59 |
+ (scale * (1.0 - lines * self.zoom_per_line)).clamp(self.min_scale, self.max_scale) |
|
60 |
+ } |
|
61 |
+} |
|
62 |
+ |
|
63 |
+fn button_zoom( |
|
64 |
+ mut actions: MessageReader<Action>, |
|
65 |
+ camera: Single<(&mut Projection, &CameraController)>, |
|
66 |
+) { |
|
67 |
+ let (mut projection, controller) = camera.into_inner(); |
|
68 |
+ let projection = ortho(&mut projection); |
|
69 |
+ for action in actions.read() { |
|
70 |
+ let lines = match action { |
|
71 |
+ Action::ZoomIn => BUTTON_ZOOM_LINES, |
|
72 |
+ Action::ZoomOut => -BUTTON_ZOOM_LINES, |
|
73 |
+ _ => continue, |
|
74 |
+ }; |
|
75 |
+ projection.scale = controller.zoomed(projection.scale, lines); |
|
76 |
+ } |
|
77 |
+} |
|
78 |
+ |
| 53 |
79 |
fn spawn_camera(mut commands: Commands, map: Res<WorldMap>) { |
| 54 |
80 |
commands.spawn(( |
| 55 |
81 |
Camera2d, |
| @@ -144,8 +170,7 @@ fn scroll_zoom( |
| 144 |
170 |
let (camera, global, mut transform, mut projection, controller) = camera.into_inner(); |
| 145 |
171 |
let projection = ortho(&mut projection); |
| 146 |
172 |
let old_scale = projection.scale; |
| 147 |
|
- let new_scale = (old_scale * (1.0 - lines * controller.zoom_per_line)) |
| 148 |
|
- .clamp(controller.min_scale, controller.max_scale); |
|
173 |
+ let new_scale = controller.zoomed(old_scale, lines); |
| 149 |
174 |
projection.scale = new_scale; |
| 150 |
175 |
|
| 151 |
176 |
let focus = window |
| @@ -188,6 +213,21 @@ mod tests { |
| 188 |
213 |
} |
| 189 |
214 |
|
| 190 |
215 |
#[test] |
|
216 |
+ fn zoom_buttons_zoom_in_steps_within_limits() { |
|
217 |
+ let controller = CameraController::default(); |
|
218 |
+ assert!(controller.zoomed(1.0, BUTTON_ZOOM_LINES) < 1.0); |
|
219 |
+ assert!(controller.zoomed(1.0, -BUTTON_ZOOM_LINES) > 1.0); |
|
220 |
+ assert_eq!( |
|
221 |
+ controller.zoomed(controller.min_scale, 10.0), |
|
222 |
+ controller.min_scale |
|
223 |
+ ); |
|
224 |
+ assert_eq!( |
|
225 |
+ controller.zoomed(controller.max_scale, -10.0), |
|
226 |
+ controller.max_scale |
|
227 |
+ ); |
|
228 |
+ } |
|
229 |
+ |
|
230 |
+ #[test] |
| 191 |
231 |
fn zooming_keeps_the_point_under_the_cursor_fixed() { |
| 192 |
232 |
let camera = Vec2::new(100.0, 50.0); |
| 193 |
233 |
let cursor_offset = Vec2::new(200.0, -120.0); |