-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
# Objective This PR addresses the 2D part of #12658. I plan to separate the examples and make one PR per camera example. ## Solution Added a new top-down example composed of: - [x] Player keyboard movements - [x] UI for keyboard instructions - [x] Colors and bloom effect to see the movement of the player - [x] Camera smooth movement towards the player (lerp) ## Testing ```bash cargo run --features="wayland,bevy/dynamic_linking" --example 2d_top_down_camera ``` https://github.com/bevyengine/bevy/assets/10638479/95db0587-e5e0-4f55-be11-97444b795793
- Loading branch information
1 parent
91db570
commit be65fbb
Showing
3 changed files
with
167 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
//! This example showcases a 2D top-down camera with smooth player tracking. | ||
//! | ||
//! ## Controls | ||
//! | ||
//! | Key Binding | Action | | ||
//! |:---------------------|:--------------| | ||
//! | `Z`(azerty), `W`(US) | Move forward | | ||
//! | `S` | Move backward | | ||
//! | `Q`(azerty), `A`(US) | Move left | | ||
//! | `D` | Move right | | ||
|
||
use bevy::core_pipeline::bloom::BloomSettings; | ||
use bevy::math::vec3; | ||
use bevy::prelude::*; | ||
use bevy::sprite::{MaterialMesh2dBundle, Mesh2dHandle}; | ||
|
||
/// Player movement speed factor. | ||
const PLAYER_SPEED: f32 = 100.; | ||
|
||
/// Camera lerp factor. | ||
const CAM_LERP_FACTOR: 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(MaterialMesh2dBundle { | ||
mesh: Mesh2dHandle(meshes.add(Rectangle::new(1000., 700.))), | ||
material: materials.add(Color::srgb(0.2, 0.2, 0.3)), | ||
..default() | ||
}); | ||
|
||
// Player | ||
commands.spawn(( | ||
Player, | ||
MaterialMesh2dBundle { | ||
mesh: meshes.add(Circle::new(25.)).into(), | ||
material: materials.add(Color::srgb(6.25, 9.4, 9.1)), // RGB values exceed 1 to achieve a bright color for the bloom effect | ||
transform: Transform { | ||
translation: vec3(0., 0., 2.), | ||
..default() | ||
}, | ||
..default() | ||
}, | ||
)); | ||
} | ||
|
||
fn setup_instructions(mut commands: Commands) { | ||
commands.spawn( | ||
TextBundle::from_section( | ||
"Move the light with ZQSD or WASD.\nThe camera will smoothly track the light.", | ||
TextStyle::default(), | ||
) | ||
.with_style(Style { | ||
position_type: PositionType::Absolute, | ||
bottom: Val::Px(12.0), | ||
left: Val::Px(12.0), | ||
..default() | ||
}), | ||
); | ||
} | ||
|
||
fn setup_camera(mut commands: Commands) { | ||
commands.spawn(( | ||
Camera2dBundle { | ||
camera: Camera { | ||
hdr: true, // HDR is required for the bloom effect | ||
..default() | ||
}, | ||
..default() | ||
}, | ||
BloomSettings::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 interpolation between | ||
// the camera position and the player position on the x and y axes. | ||
// Here we use the in-game time, to get the elapsed time (in seconds) | ||
// since the previous update. This avoids jittery movement when tracking | ||
// the player. | ||
camera.translation = camera | ||
.translation | ||
.lerp(direction, time.delta_seconds() * CAM_LERP_FACTOR); | ||
} | ||
|
||
/// Update the player position with keyboard inputs. | ||
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_seconds(); | ||
player.translation += move_delta.extend(0.); | ||
} |