Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/camera/free_camera_controller.rs
7223 views
1
//! This example showcases the default `FreeCamera` camera controller.
2
//!
3
//! The default `FreeCamera` controller is useful for exploring large scenes, debugging and editing purposes. To use it,
4
//! simply add the [`FreeCameraPlugin`] to your [`App`] and attach the [`FreeCamera`] component to the camera entity you
5
//! wish to control.
6
//!
7
//! ## Default Controls
8
//!
9
//! This controller has a simple 6-axis control scheme, and mouse controls for camera orientation. There are also
10
//! bindings for capturing the mouse, both while holding the button and toggle, a run feature that increases the
11
//! max speed, and scrolling changes the movement speed. All keybinds can be changed by editing the [`FreeCamera`]
12
//! component.
13
//!
14
//! | Default Key Binding | Action |
15
//! |:--------------------|:-----------------------|
16
//! | Mouse | Look around |
17
//! | Left click | Capture mouse (hold) |
18
//! | M | Capture mouse (toggle) |
19
//! | WASD | Horizontal movement |
20
//! | QE | Vertical movement |
21
//! | Left shift | Run |
22
//! | Scroll wheel | Change movement speed |
23
//!
24
//! The movement speed, sensitivity and friction can also be changed by the [`FreeCamera`] component.
25
//!
26
//! ## Example controls
27
//!
28
//! This example also provides a few extra keybinds to change the camera sensitivity, friction (how fast the camera
29
//! stops), scroll factor (how much scrolling changes speed) and enabling/disabling the controller.
30
//!
31
//! | Key Binding | Action |
32
//! |:------------|:-----------------------|
33
//! | Z | Decrease sensitivity |
34
//! | X | Increase sensitivity |
35
//! | C | Decrease friction |
36
//! | V | Increase friction |
37
//! | F | Decrease scroll factor |
38
//! | G | Increase scroll factor |
39
//! | B | Enable/Disable |
40
41
use std::f32::consts::{FRAC_PI_4, PI};
42
43
use bevy::{
44
camera_controller::free_camera::{FreeCamera, FreeCameraPlugin, FreeCameraState},
45
color::palettes::tailwind,
46
prelude::*,
47
};
48
49
fn main() {
50
App::new()
51
.add_plugins(DefaultPlugins)
52
// Plugin that enables FreeCamera functionality
53
.add_plugins(FreeCameraPlugin)
54
// Example code plugins
55
.add_plugins((CameraPlugin, CameraSettingsPlugin, ScenePlugin))
56
.run();
57
}
58
59
// Plugin that spawns the camera.
60
struct CameraPlugin;
61
impl Plugin for CameraPlugin {
62
fn build(&self, app: &mut App) {
63
app.add_systems(Startup, spawn_camera);
64
}
65
}
66
67
fn spawn_camera(mut commands: Commands) {
68
commands.spawn((
69
Camera3d::default(),
70
Transform::from_xyz(0.0, 1.0, 0.0).looking_to(Vec3::X, Vec3::Y),
71
// This component stores all camera settings and state, which is used by the FreeCameraPlugin to
72
// control it. These properties can be changed at runtime, but beware the controller system is
73
// constantly using and modifying those values unless the enabled field is false.
74
FreeCamera {
75
sensitivity: 0.2,
76
friction: 25.0,
77
walk_speed: 3.0,
78
run_speed: 9.0,
79
..default()
80
},
81
));
82
}
83
84
// Plugin that handles camera settings controls and information text
85
struct CameraSettingsPlugin;
86
impl Plugin for CameraSettingsPlugin {
87
fn build(&self, app: &mut App) {
88
app.add_systems(PostStartup, spawn_text)
89
.add_systems(Update, (update_camera_settings, update_text));
90
}
91
}
92
93
#[derive(Component)]
94
struct InfoText;
95
96
fn spawn_text(mut commands: Commands, free_camera_query: Query<&FreeCamera>) {
97
commands.spawn((
98
Node {
99
position_type: PositionType::Absolute,
100
top: px(-16),
101
left: px(12),
102
..default()
103
},
104
children![Text::new(format!(
105
"{}",
106
free_camera_query.single().unwrap()
107
))],
108
));
109
commands.spawn((
110
Node {
111
position_type: PositionType::Absolute,
112
bottom: px(12),
113
left: px(12),
114
..default()
115
},
116
children![Text::new(concat![
117
"Z/X: decrease/increase sensitivity\n",
118
"C/V: decrease/increase friction\n",
119
"F/G: decrease/increase scroll factor\n",
120
"B: enable/disable controller",
121
]),],
122
));
123
124
// Mutable text marked with component
125
commands.spawn((
126
Node {
127
position_type: PositionType::Absolute,
128
top: px(12),
129
right: px(12),
130
..default()
131
},
132
children![(InfoText, Text::new(""))],
133
));
134
}
135
136
fn update_camera_settings(
137
mut camera_query: Query<(&mut FreeCamera, &mut FreeCameraState)>,
138
input: Res<ButtonInput<KeyCode>>,
139
) {
140
let (mut free_camera, mut free_camera_state) = camera_query.single_mut().unwrap();
141
142
if input.pressed(KeyCode::KeyZ) {
143
free_camera.sensitivity = (free_camera.sensitivity - 0.005).max(0.005);
144
}
145
if input.pressed(KeyCode::KeyX) {
146
free_camera.sensitivity += 0.005;
147
}
148
if input.pressed(KeyCode::KeyC) {
149
free_camera.friction = (free_camera.friction - 0.2).max(0.0);
150
}
151
if input.pressed(KeyCode::KeyV) {
152
free_camera.friction += 0.2;
153
}
154
if input.pressed(KeyCode::KeyF) {
155
free_camera.scroll_factor = (free_camera.scroll_factor - 0.02).max(0.02);
156
}
157
if input.pressed(KeyCode::KeyG) {
158
free_camera.scroll_factor += 0.02;
159
}
160
if input.just_pressed(KeyCode::KeyB) {
161
free_camera_state.enabled = !free_camera_state.enabled;
162
}
163
}
164
165
fn update_text(
166
mut text_query: Query<&mut Text, With<InfoText>>,
167
camera_query: Query<(&FreeCamera, &FreeCameraState)>,
168
) {
169
let mut text = text_query.single_mut().unwrap();
170
171
let (free_camera, free_camera_state) = camera_query.single().unwrap();
172
173
text.0 = format!(
174
"Enabled: {},\nSensitivity: {:.03}\nFriction: {:.01}\nScroll factor: {:.02}\nWalk Speed: {:.02}\nRun Speed: {:.02}\nSpeed: {:.02}",
175
free_camera_state.enabled,
176
free_camera.sensitivity,
177
free_camera.friction,
178
free_camera.scroll_factor,
179
free_camera.walk_speed,
180
free_camera.run_speed,
181
free_camera_state.velocity.length(),
182
);
183
}
184
185
// Plugin that spawns the scene and lighting.
186
struct ScenePlugin;
187
impl Plugin for ScenePlugin {
188
fn build(&self, app: &mut App) {
189
app.add_systems(Startup, (spawn_lights, spawn_world));
190
}
191
}
192
193
fn spawn_lights(mut commands: Commands) {
194
// Main light
195
commands.spawn((
196
PointLight {
197
color: Color::from(tailwind::ORANGE_300),
198
shadows_enabled: true,
199
..default()
200
},
201
Transform::from_xyz(0.0, 3.0, 0.0),
202
));
203
// Light behind wall
204
commands.spawn((
205
PointLight {
206
color: Color::WHITE,
207
shadows_enabled: true,
208
..default()
209
},
210
Transform::from_xyz(-3.5, 3.0, 0.0),
211
));
212
// Light under floor
213
commands.spawn((
214
PointLight {
215
color: Color::from(tailwind::RED_300),
216
shadows_enabled: true,
217
..default()
218
},
219
Transform::from_xyz(0.0, -0.5, 0.0),
220
));
221
}
222
223
fn spawn_world(
224
mut commands: Commands,
225
mut materials: ResMut<Assets<StandardMaterial>>,
226
mut meshes: ResMut<Assets<Mesh>>,
227
) {
228
let cube = meshes.add(Cuboid::new(1.0, 1.0, 1.0));
229
let floor = meshes.add(Plane3d::new(Vec3::Y, Vec2::splat(10.0)));
230
let sphere = meshes.add(Sphere::new(0.5));
231
let wall = meshes.add(Cuboid::new(0.2, 4.0, 3.0));
232
233
let blue_material = materials.add(Color::from(tailwind::BLUE_700));
234
let red_material = materials.add(Color::from(tailwind::RED_950));
235
let white_material = materials.add(Color::WHITE);
236
237
// Top side of floor
238
commands.spawn((
239
Mesh3d(floor.clone()),
240
MeshMaterial3d(white_material.clone()),
241
));
242
// Under side of floor
243
commands.spawn((
244
Mesh3d(floor.clone()),
245
MeshMaterial3d(white_material.clone()),
246
Transform::from_xyz(0.0, -0.01, 0.0).with_rotation(Quat::from_rotation_x(PI)),
247
));
248
// Blue sphere
249
commands.spawn((
250
Mesh3d(sphere.clone()),
251
MeshMaterial3d(blue_material.clone()),
252
Transform::from_xyz(3.0, 1.5, 0.0),
253
));
254
// Tall wall
255
commands.spawn((
256
Mesh3d(wall.clone()),
257
MeshMaterial3d(white_material.clone()),
258
Transform::from_xyz(-3.0, 2.0, 0.0),
259
));
260
// Cube behind wall
261
commands.spawn((
262
Mesh3d(cube.clone()),
263
MeshMaterial3d(blue_material.clone()),
264
Transform::from_xyz(-4.2, 0.5, 0.0),
265
));
266
// Hidden cube under floor
267
commands.spawn((
268
Mesh3d(cube.clone()),
269
MeshMaterial3d(red_material.clone()),
270
Transform {
271
translation: Vec3::new(3.0, -2.0, 0.0),
272
rotation: Quat::from_euler(EulerRot::YXZEx, FRAC_PI_4, FRAC_PI_4, 0.0),
273
..default()
274
},
275
));
276
}
277
278