docs/bevy/examples/camera/2d_top_down_camera.rs 3.5 K · 116 lines · raw · history

1 //! This example showcases a 2D top-down camera with smooth player tracking.
2 //!
3 //! ## Controls
4 //!
5 //! | Key Binding | Action |
6 //! |:---------------------|:--------------|
7 //! | `W` | Move up |
8 //! | `S` | Move down |
9 //! | `A` | Move left |
10 //! | `D` | Move right |
11
12 use bevy::{post_process::bloom::Bloom, prelude::*};
13
14 /// Player movement speed factor.
15 const PLAYER_SPEED: f32 = 100.;
16
17 /// How quickly should the camera snap to the desired location.
18 const CAMERA_DECAY_RATE: f32 = 2.;
19
20 #[derive(Component)]
21 struct Player;
22
23 fn main() {
24 App::new()
25 .add_plugins(DefaultPlugins)
26 .add_systems(Startup, (setup_scene, setup_instructions, setup_camera))
27 .add_systems(Update, (move_player, update_camera).chain())
28 .run();
29 }
30
31 fn setup_scene(
32 mut commands: Commands,
33 mut meshes: ResMut<Assets<Mesh>>,
34 mut materials: ResMut<Assets<ColorMaterial>>,
35 ) {
36 // World where we move the player
37 commands.spawn((
38 Mesh2d(meshes.add(Rectangle::new(1000., 700.))),
39 MeshMaterial2d(materials.add(Color::srgb(0.2, 0.2, 0.3))),
40 ));
41
42 // Player
43 commands.spawn((
44 Player,
45 Mesh2d(meshes.add(Circle::new(25.))),
46 MeshMaterial2d(materials.add(Color::srgb(6.25, 9.4, 9.1))), // RGB values exceed 1 to achieve a bright color for the bloom effect
47 Transform::from_xyz(0., 0., 2.),
48 ));
49 }
50
51 fn setup_instructions(mut commands: Commands) {
52 commands.spawn((
53 Text::new("Move the light with WASD.\nThe camera will smoothly track the light."),
54 Node {
55 position_type: PositionType::Absolute,
56 bottom: px(12),
57 left: px(12),
58 ..default()
59 },
60 ));
61 }
62
63 fn setup_camera(mut commands: Commands) {
64 commands.spawn((Camera2d, Bloom::NATURAL));
65 }
66
67 /// Update the camera position by tracking the player.
68 fn update_camera(
69 mut camera: Single<&mut Transform, (With<Camera2d>, Without<Player>)>,
70 player: Single<&Transform, (With<Player>, Without<Camera2d>)>,
71 time: Res<Time>,
72 ) {
73 let Vec3 { x, y, .. } = player.translation;
74 let direction = Vec3::new(x, y, camera.translation.z);
75
76 // Applies a smooth effect to camera movement using stable interpolation
77 // between the camera position and the player position on the x and y axes.
78 camera
79 .translation
80 .smooth_nudge(&direction, CAMERA_DECAY_RATE, time.delta_secs());
81 }
82
83 /// Update the player position with keyboard inputs.
84 /// Note that the approach used here is for demonstration purposes only,
85 /// as the point of this example is to showcase the camera tracking feature.
86 ///
87 /// A more robust solution for player movement can be found in `examples/movement/physics_in_fixed_timestep.rs`.
88 fn move_player(
89 mut player: Single<&mut Transform, With<Player>>,
90 time: Res<Time>,
91 kb_input: Res<ButtonInput<KeyCode>>,
92 ) {
93 let mut direction = Vec2::ZERO;
94
95 if kb_input.pressed(KeyCode::KeyW) {
96 direction.y += 1.;
97 }
98
99 if kb_input.pressed(KeyCode::KeyS) {
100 direction.y -= 1.;
101 }
102
103 if kb_input.pressed(KeyCode::KeyA) {
104 direction.x -= 1.;
105 }
106
107 if kb_input.pressed(KeyCode::KeyD) {
108 direction.x += 1.;
109 }
110
111 // Progressively update the player's position over time. Normalize the
112 // direction vector to prevent it from exceeding a magnitude of 1 when
113 // moving diagonally.
114 let move_delta = direction.normalize_or_zero() * PLAYER_SPEED * time.delta_secs();
115 player.translation += move_delta.extend(0.);
116 }