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