1
- //! This example demonstrates how to use the `Camera::viewport_to_world_2d` method.
1
+ //! This example demonstrates how to use the `Camera::viewport_to_world_2d` method with a dynamic viewport and camera .
2
2
3
- use bevy:: { color:: palettes:: basic:: WHITE , prelude:: * } ;
3
+ use bevy:: {
4
+ color:: palettes:: {
5
+ basic:: WHITE ,
6
+ css:: { GREEN , RED } ,
7
+ } ,
8
+ math:: ops:: powf,
9
+ prelude:: * ,
10
+ render:: camera:: Viewport ,
11
+ } ;
4
12
5
13
fn main ( ) {
6
14
App :: new ( )
7
15
. add_plugins ( DefaultPlugins )
8
16
. add_systems ( Startup , setup)
9
- . add_systems ( Update , draw_cursor)
17
+ . add_systems ( FixedUpdate , controls)
18
+ . add_systems (
19
+ PostUpdate ,
20
+ draw_cursor. after ( TransformSystem :: TransformPropagate ) ,
21
+ )
10
22
. run ( ) ;
11
23
}
12
24
@@ -15,35 +27,165 @@ fn draw_cursor(
15
27
window : Query < & Window > ,
16
28
mut gizmos : Gizmos ,
17
29
) {
30
+ let ( camera, camera_transform) = * camera_query;
18
31
let Ok ( window) = window. get_single ( ) else {
19
32
return ;
20
33
} ;
21
34
22
- let ( camera, camera_transform) = * camera_query;
23
-
24
35
let Some ( cursor_position) = window. cursor_position ( ) else {
25
36
return ;
26
37
} ;
27
38
28
39
// Calculate a world position based on the cursor's position.
29
- let Ok ( point) = camera. viewport_to_world_2d ( camera_transform, cursor_position) else {
40
+ let Ok ( world_pos) = camera. viewport_to_world_2d ( camera_transform, cursor_position) else {
41
+ return ;
42
+ } ;
43
+
44
+ // To test Camera::world_to_viewport, convert result back to viewport space and then back to world space.
45
+ let Ok ( viewport_check) = camera. world_to_viewport ( camera_transform, world_pos. extend ( 0.0 ) )
46
+ else {
47
+ return ;
48
+ } ;
49
+ let Ok ( world_check) = camera. viewport_to_world_2d ( camera_transform, viewport_check. xy ( ) ) else {
50
+ return ;
51
+ } ;
52
+
53
+ gizmos. circle_2d ( world_pos, 10. , WHITE ) ;
54
+ // Should be the same as world_pos
55
+ gizmos. circle_2d ( world_check, 8. , RED ) ;
56
+ }
57
+
58
+ fn controls (
59
+ mut camera_query : Query < ( & mut Camera , & mut Transform , & mut Projection ) > ,
60
+ window : Query < & Window > ,
61
+ input : Res < ButtonInput < KeyCode > > ,
62
+ time : Res < Time < Fixed > > ,
63
+ ) {
64
+ let Ok ( window) = window. get_single ( ) else {
65
+ return ;
66
+ } ;
67
+ let Ok ( ( mut camera, mut transform, mut projection) ) = camera_query. get_single_mut ( ) else {
30
68
return ;
31
69
} ;
70
+ let fspeed = 600.0 * time. delta_secs ( ) ;
71
+ let uspeed = fspeed as u32 ;
72
+ let window_size = window. resolution . physical_size ( ) ;
32
73
33
- gizmos. circle_2d ( point, 10. , WHITE ) ;
74
+ // Camera movement controls
75
+ if input. pressed ( KeyCode :: ArrowUp ) {
76
+ transform. translation . y += fspeed;
77
+ }
78
+ if input. pressed ( KeyCode :: ArrowDown ) {
79
+ transform. translation . y -= fspeed;
80
+ }
81
+ if input. pressed ( KeyCode :: ArrowLeft ) {
82
+ transform. translation . x -= fspeed;
83
+ }
84
+ if input. pressed ( KeyCode :: ArrowRight ) {
85
+ transform. translation . x += fspeed;
86
+ }
87
+
88
+ // Camera zoom controls
89
+ if let Projection :: Orthographic ( projection2d) = & mut * projection {
90
+ if input. pressed ( KeyCode :: Comma ) {
91
+ projection2d. scale *= powf ( 4.0f32 , time. delta_secs ( ) ) ;
92
+ }
93
+
94
+ if input. pressed ( KeyCode :: Period ) {
95
+ projection2d. scale *= powf ( 0.25f32 , time. delta_secs ( ) ) ;
96
+ }
97
+ }
98
+
99
+ if let Some ( viewport) = camera. viewport . as_mut ( ) {
100
+ // Viewport movement controls
101
+ if input. pressed ( KeyCode :: KeyW ) {
102
+ viewport. physical_position . y = viewport. physical_position . y . saturating_sub ( uspeed) ;
103
+ }
104
+ if input. pressed ( KeyCode :: KeyS ) {
105
+ viewport. physical_position . y += uspeed;
106
+ }
107
+ if input. pressed ( KeyCode :: KeyA ) {
108
+ viewport. physical_position . x = viewport. physical_position . x . saturating_sub ( uspeed) ;
109
+ }
110
+ if input. pressed ( KeyCode :: KeyD ) {
111
+ viewport. physical_position . x += uspeed;
112
+ }
113
+
114
+ // Bound viewport position so it doesn't go off-screen
115
+ viewport. physical_position = viewport
116
+ . physical_position
117
+ . min ( window_size - viewport. physical_size ) ;
118
+
119
+ // Viewport size controls
120
+ if input. pressed ( KeyCode :: KeyI ) {
121
+ viewport. physical_size . y = viewport. physical_size . y . saturating_sub ( uspeed) ;
122
+ }
123
+ if input. pressed ( KeyCode :: KeyK ) {
124
+ viewport. physical_size . y += uspeed;
125
+ }
126
+ if input. pressed ( KeyCode :: KeyJ ) {
127
+ viewport. physical_size . x = viewport. physical_size . x . saturating_sub ( uspeed) ;
128
+ }
129
+ if input. pressed ( KeyCode :: KeyL ) {
130
+ viewport. physical_size . x += uspeed;
131
+ }
132
+
133
+ // Bound viewport size so it doesn't go off-screen
134
+ viewport. physical_size = viewport
135
+ . physical_size
136
+ . min ( window_size - viewport. physical_position )
137
+ . max ( UVec2 :: new ( 20 , 20 ) ) ;
138
+ }
34
139
}
35
140
36
- fn setup ( mut commands : Commands ) {
37
- commands. spawn ( Camera2d ) ;
141
+ fn setup (
142
+ mut commands : Commands ,
143
+ mut meshes : ResMut < Assets < Mesh > > ,
144
+ mut materials : ResMut < Assets < ColorMaterial > > ,
145
+ window : Single < & Window > ,
146
+ ) {
147
+ let window_size = window. resolution . physical_size ( ) . as_vec2 ( ) ;
148
+
149
+ // Initialize centered, non-window-filling viewport
150
+ commands. spawn ( (
151
+ Camera2d ,
152
+ Camera {
153
+ viewport : Some ( Viewport {
154
+ physical_position : ( window_size * 0.125 ) . as_uvec2 ( ) ,
155
+ physical_size : ( window_size * 0.75 ) . as_uvec2 ( ) ,
156
+ ..default ( )
157
+ } ) ,
158
+ ..default ( )
159
+ } ,
160
+ ) ) ;
38
161
39
162
// Create a minimal UI explaining how to interact with the example
40
163
commands. spawn ( (
41
- Text :: new ( "Move the mouse to see the circle follow your cursor." ) ,
164
+ Text :: new (
165
+ "Move the mouse to see the circle follow your cursor.\n \
166
+ Use the arrow keys to move the camera.\n \
167
+ Use the comma and period keys to zoom in and out.\n \
168
+ Use the WASD keys to move the viewport.\n \
169
+ Use the IJKL keys to resize the viewport.",
170
+ ) ,
42
171
Node {
43
172
position_type : PositionType :: Absolute ,
44
173
top : Val :: Px ( 12.0 ) ,
45
174
left : Val :: Px ( 12.0 ) ,
46
175
..default ( )
47
176
} ,
48
177
) ) ;
178
+
179
+ // Add mesh to make camera movement visible
180
+ commands. spawn ( (
181
+ Mesh2d ( meshes. add ( Rectangle :: new ( 40.0 , 20.0 ) ) ) ,
182
+ MeshMaterial2d ( materials. add ( Color :: from ( GREEN ) ) ) ,
183
+ ) ) ;
184
+
185
+ // Add background to visualize viewport bounds
186
+ commands. spawn ( (
187
+ Mesh2d ( meshes. add ( Rectangle :: new ( 50000.0 , 50000.0 ) ) ) ,
188
+ MeshMaterial2d ( materials. add ( Color :: linear_rgb ( 0.01 , 0.01 , 0.01 ) ) ) ,
189
+ Transform :: from_translation ( Vec3 :: new ( 0.0 , 0.0 , -200.0 ) ) ,
190
+ ) ) ;
49
191
}
0 commit comments