bevy/examples/camera/2d_top_down_camera.rs
andristarr 7482a0d26d
aligning public apis of Time,Timer and Stopwatch (#15962)
Fixes #15834

## Migration Guide

The APIs of `Time`, `Timer` and `Stopwatch` have been cleaned up for
consistency with each other and the standard library's `Duration` type.
The following methods have been renamed:

- `Stowatch::paused` -> `Stopwatch::is_paused`
- `Time::elapsed_seconds` -> `Time::elasped_secs` (including `_f64` and
`_wrapped` variants)
2024-10-16 21:09:32 +00:00

135 lines
3.9 KiB
Rust

//! 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."),
Style {
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.);
}